Archive for the 'spring' Category

30
Abr
08

Spring 2.5 y los filtros de contexto

El cambio a spring 2.5 conlleva una nueva forma de cargar tus clases como beans, lo que ellos llaman contexto. Este contexto es el responsable de añadir como beans las clases que tengas marcadas con las anotaciones que spring te da de serie(@Component, @Controller, @Service, @Repository). Si buceamos un poco en el código de spring podemos ver que la clase encargada de hacer esto es ClassPathBeanDefinitionScanner.java. Esta clase tiene implementada una pequeña jerarquía de filtros para añadir o quitar clases del contexto cuando arranca nuestra aplicación. Por defecto, añade todas las clases que estén marcadas con las anotaciones que hemos visto anteriormente:


  this.includeFilters.add(new AnnotationTypeFilter(Component.class));

Ahora vamos a ver las opciones que nos da el framework para añadir clases a ese contexto. En su documentación podemos ver un claro ejemplo de como añadir una nueva anotación:


  <context:component-scan>
    <context:include-filter type="annotation"
      expression="CustomAnnotation"/>
  </context:component-scan>

Lo que viene a decir el xml es que al contexto le vamos a añadir un filtro de tipo anotación y para todas las clases que estén marcadas con la anotación que va dentro del atributo expression.

Con lo que él internamente hará algo tal que así:


  this.includeFilters.add(new AnnotationTypeFilter(CustomAnnotation.class));

Lo que no se explica en la documentación es que por defecto, la clase que se encarga de parsear el contexto, ComponentScanBeanDefinitionParser.java, también soporta otros tipos de filtros además del de anotaciones.

El más sencillo sería el filtro de asignación, que simplemente añadiría una clase o un grupo de clases que extiendan o implementen algo común:


  <context:component-scan>
    <context:include-filter type="assignable"
      expression="org.myapp.IService"/>
  </context:component-scan>

Con este código se añadirían todas las clases que extendieran de la interfaz org.myapp.IService.

Otro de los filtros que soporta es por expresiones regulares, con este filtro spring cargará en el contexto las clases que coincidan con una expresión regular determinada:


  <context:component-scan>
    <context:include-filter type="regex"
      expression="(Service|Controller)\w+"/>
  </context:component-scan>

Con este código se cargarán todas clases que empiecen por Service o Coltroller.

Por último, tenemos un filtro un poco más complejo que se encarga de evaluar expresiones propias de Aspect Oriented Programming(AOP):


  <context:component-scan>
    <context:include-filter type="aspectj"
      expression="Service || Controller"/>
  </context:component-scan>

Con lo que cargaremos todas las clases que extiendan o implementen la clase Service o Controller.

La verdad es que con el grupo de anotaciones que spring trae de serie es más que suficiente para resolver la mayoría de los problemas pero nos podemos encontrar en escenarios donde no queremos tener como dependencia spring en una clase que puede ser utilizada tanto por spring como por otra aplicación o módulo que no use spring, con lo que acceder a los filtros del contexto nos vendrá muy bien.

Anuncios
27
Feb
08

sobre spring 2.5, anotaciones y expresiones regurales

Una de las nuevas características de la última versión estable de Spring MVC es la posibilidad de configurar la relación entre una url y el método que se ejecuta mediante la anotación @RequestMapping. Sobre las nuevas características hay muy buenos artículos, como el aparecido en infoq o en el mismo blog de springsource. Pero lo que estos artículos no explican es como configurar nuestra aplicación para que esta anotación acepte como valores expresiones regulares.

En este artículo vamos describir los componentes que se encargan de estas relaciones y como añadir nuevas clases para poder trabajar con expresiones regulares.

El primer paso es crear una clase que se encargue de machear las expresiones que vamos a identificar como rutas válidas. Por defecto la clase AntPathMatcher se encarga de machear expresiones tipo ant(‘/**’, ‘/home/*’, ..) como no queremos perder esta funcionalidad podemos extender nuestra clase de esta, sobreescribir el método doMatch y marcarla como un componente con la anotación @Component:


@Component("regexPathMatcher")
public class RegexPathMatcher extends AntPathMatcher {
  @Override
  protected boolean doMatch(String pattern, String path, boolean fullMatch) {
    boolean match = super.doMatch(pattern, path, fullMatch);
      if (!match) {
        jregex.Pattern pat = new jregex.Pattern(pattern);
        match = pat.matcher(path).matches();
      }
    return match;
  }
}

Las clases encargadas de relacionar los controladores con las rutas que resuelven siempre extienden de la interfaz HandlerAdapter. En el caso de que estemos usando este sistema de anotaciones la clase por defecto encargada será AnnotationMethodHandlerAdapter. A esta clase le tenemos que indicar que el componente encargado de machear las rutas ya no es el de por defecto, para esto declararemos este componente en el xml de configuración y le inyectaremos el nuevo matcher:


<bean id="regexMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="pathMatcher" ref="regexPathMatcher"/>
</bean>

El último componente que hay que configurar es el encargado de decidir si la ruta que llega de una petición es válida en la aplicación. Estos componentes extienden de la clase abstracta AbstractHandlerMapping, en el caso de usar anotaciones el componente por defecto es DefaultAnnotationHandlerMapping. Para configurarlo dentro del xml tendremos que añadir:


<bean id="regexUrlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
  <property name="alwaysUseFullPath" value="true"/>
  <property name="pathMatcher" ref="regexPathMatcher"/>
</bean>

Una vez hecho esto nuestra aplicación soportará rutas un poco más elaboradas como estas:


@RequestMapping("/home(/page({=page}\\d+))?")
@RequestMapping("/user/({=user}[\\w\\d_-.]+)/(contacts|followers)")