2011-09-23 16 views
5

Ho questo in src/main/scanalato/...Spring MVC controllore annotato scanalato

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

@Controller 
class GroovyController { 

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

Uso Maven 3 e molla 3.1 (Milestone). Spring MVC funziona perfettamente per i controller java e tutto è impostato bene. La classe groovy viene compilata correttamente e può essere trovata nella directory classes insieme alle classi del controller java.

Ho un controller simile scritto in java (JavaController) nello stesso pacchetto ma in src/main/java e viene rilevato correttamente dalla primavera e mappato e posso vedere la risposta sullo schermo quando premo l'url.

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

@Controller 
class JavaController { 

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

Jetty si avvia normalmente senza errori nel registro, ma in non vedo url groove sempre mappati mentre posso vedere quello 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() 

Tutte le impostazioni vanno bene come in altre parti del app stanno lavorando molto bene con annotazioni (componente-scan, ecc), solo che non posso ottenere l'url mappato in GroovyController

Qualcuno può spiegare che cosa deve essere fatto al fine di ottenere Controller s scritto in groovy funzionante?

PS: sto evitando che GroovyServlet esegua gli script perché presenta uno svantaggio importante quando si tratta dell'iniezione di bean e delle mappature dei percorsi di url.

risposta

1

Sfortunatamente, se si desidera eseguire questa operazione in Groovy, è necessario creare un'interfaccia per la classe Controller e annotare anche le definizioni del metodo. Spring crea un proxy per la tua classe usando Cglib. Tuttavia, senza creare un'interfaccia personalizzata per il controller, Spring esegue il proxy su groovy.lang.GroovyObject poiché tutti gli oggetti Groovy implementano tale interfaccia per impostazione predefinita.

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 tutto il rispetto per Ben (con cui lavoro), il problema qui non è che Spring sta creando un proxy cglib. Piuttosto, sta creando un proxy dinamico JDK (o basato su interfaccia). Questo metodo di creazione dei proxy può solo implementare i metodi dichiarati nelle interfacce implementate del target. In effetti, desidera Spring per creare un proxy cglib, che crea un proxy che è una sottoclasse dell'oggetto di destinazione e può quindi ricreare tutti i suoi metodi pubblici. Se non diversamente specificato, Spring creerà un proxy cglib se l'oggetto di destinazione non implementa alcuna interfaccia e altrimenti un proxy basato su interfaccia. Poiché tutti gli oggetti Groovy implementano GroovyObject, si ottiene un proxy basato sull'interfaccia, anche se non è stata implementata esplicitamente alcuna interfaccia nel controller Groovy. La soluzione di Ben è corretta nel senso che se crei un'interfaccia con tutti i metodi del controller, otterrai il comportamento previsto. Un'alternativa è creare un BeanFactoryPostProcessor che istruisca Spring a creare proxy cglib per le classi che implementano GroovyObject e solo GroovyObject. Ecco il codice:

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

Basta includere un bean di questo tipo nel proprio contesto e voilà! È possibile disporre di controller Groovy senza la necessità di definire interfacce.

+0

ti suggerisco di aggiungere la definizione di fagioli per la tua risposta. – Zeki

2

Mi permetto di dissentire. Non è necessario implementare un'interfaccia. Il problema qui è che il valore predefinito AnnotationMethodHandlerAdapter non legge le annotazioni dai proxy. Quindi dovremmo creare questo proxy AnnotationMethodHandlerAdapter che estende il valore predefinito di AnnotationMethodHandlerAdapter di primavera. Abbiamo anche bisogno di istanziare un bean per questo ProxyAwareAnnotationMethodHandlerAdapter nel file xml di Spring Configuration. Nota: questa funzione non è disponibile in Spring 3.x ma poiché la primavera 4.0 supporta i groovy bean, questa funzione dovrebbe essere coperta.

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

    } 

Il file XML di configurazione della molla deve avere il seguente. Invece di creare un bean di AnnotationMethodHandlerAdapter, dobbiamo creare 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> 

Anche Spring analizza il file XML di configurazione utilizzando un parser SAX (basato sulla presenza di eventi). Quindi, affinché la molla capisca le annotazioni all'interno degli script groovy, i bean groovy (usando il tag) devono essere creati dopo ProxyAwareAnnotationMethodHandlerAdapter.

Speranza che aiuta

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

+0

Fantastico! Quella lezione ha reso la mia giornata. Ora ho controller e servizi groovy. Grazie – Jason