web-dev-qa-db-fra.com

Est-il possible de câbler un intercepteur Spring MVC à l'aide d'annotations?

Est-il possible de câbler un intercepteur Spring MVC à l'aide d'annotations et, dans l'affirmative, quelqu'un pourrait-il me donner un exemple de procédure?

Par fil via des annotations, je parle de faire le moins possible dans la configuration XML. Par exemple, dans ce fichier de configuration, j'ai trouvé à http://www.vaannila.com/spring/spring-interceptors.html ;

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:interceptors-ref="loggerInterceptor" />
<bean id="loggerInterceptor" class="com.vaannila.interceptor.LoggerInterceptor" />

Comment peu de configuration pourriez-vous sortir avec là? J'imagine qu'un @Autowired supprimerait la nécessité de déclarer explicitement le bean dans la ligne 2, mais serait-il possible de supprimer également la ligne 1 avec une annotation?

27
James McMahon

Autant que je sache, il n'y a aucun moyen de configurer des intercepteurs Spring MVC sans XML.

Cependant, il existe certaines simplifications avec l'espace de noms mvc dans les dernières versions de Spring 3.0.x (et non de Spring 3.0.0!):

<mvc:interceptors>
    <bean class="com.vaannila.interceptor.LoggerInterceptor" />
</mvc:interceptors>

Voir également:

19
axtavt

Je suis tombé sur cette question en cherchant exactement ceci. Enfin, j'ai découvert que cela fonctionnait dans Spring 3.1 en utilisant @EnableWebMVC conjointement avec WebMvcConfigurerAdapter.

Exemple simple:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages="webapp.base.package")
public class WebApplicationConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggerInterceptor());
    }

}
73
Markus Kreusch

J'ai implémenté une solution de travail utilisant une annotation @Interceptor personnalisée dans l'esprit de l'annotation @Controller de Spring:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Component
public @interface Interceptor {
  String[] pathPatterns() default {};
  String[] excludePathPatterns() default {};
}

Cette annotation doit être appliquée aux types HandlerInterceptor comme ceci:

@Interceptor
public class BuildTimestampInterceptor extends HandlerInterceptorAdapter {
  private final String buildTimestamp;

  public BuildTimestampInterceptor(@Value("${build.timestamp}") String buildTimestamp) {
    this.buildTimestamp = buildTimestamp;
  }

  @Override
  public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
    req.setAttribute("buildTimestamp", buildTimestamp);
    return true;
  }
}

Enfin, la classe de processeur, InterceptorProcessor, est un bean Spring qui étend WebMvcConfigurerAdapter et implémente BeanPostProcessor afin de rechercher les annotations @Interceptor personnalisées et d'enregistrer les beans ayant cette annotation comme étant HandlerInterceptors à l'intérieur de la méthode addInterceptors remplacée:

@Component
public class InterceptorProcessor extends WebMvcConfigurerAdapter implements BeanPostProcessor {
  private final Map<HandlerInterceptor,Interceptor> interceptors = new HashMap<>();

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    scanForInterceptorAnnotation(bean, beanName);
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String string) throws BeansException {
    return bean;
  }

  protected void scanForInterceptorAnnotation(Object bean, String beanName) {
    Optional<Interceptor> optionalInterceptor = getInterceptorAnnotation(bean.getClass());
    if (optionalInterceptor.isPresent() && bean instanceof HandlerInterceptor) {
      interceptors.put((HandlerInterceptor) bean, optionalInterceptor.get());
    }
  }

  private Optional<Interceptor> getInterceptorAnnotation(Class cls) {
    Annotation[] annotations = cls.getAnnotationsByType(Interceptor.class);
    if (hasValue(annotations)) {
      return Optional.of((Interceptor) annotations[0]);
    }
    return Optional.empty();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    interceptors.forEach((HandlerInterceptor key, Interceptor val) -> {
      InterceptorRegistration registration = registry.addInterceptor(key);
      if (hasValue(val.pathPatterns())) {
        registration.addPathPatterns(val.pathPatterns());
      }

      if (hasValue(val.excludePathPatterns())) {
        registration.excludePathPatterns(val.excludePathPatterns());
      }
    });
  }

  private static <T> boolean hasValue(T[] array) {
    return array != null && array.length > 0;
  }
}

Rappelez-vous simplement que votre application Spring doit rechercher ce bean de processeur afin qu’il enregistre réellement votre @Interceptors. Quelque chose comme:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"org.my.controller", "org.my.utils.processor"})
public class WebConfig extends WebMvcConfigurerAdapter {...
3
Brice Roncace

Je ne connais pas Spring-AOP, mais si vous utilisez AspectJ via Spring, vous pouvez utiliser @Aspect, @Pointcut, @Advise et plus encore ...

vous trouverez également un article de Nice sur l'utilisation de ces annotations avec Spring AOP: http://Java-x.blogspot.com/2009/07/spring-aop-with-aspecj-annotations.html

0
fasseg