阿男的小窝

View the Project on GitHub

resteasy-links中的AddLinks功能实现

本文分析resteasy-links当中,AddLinks的功能实现。首先分析resteasy-links提供的RESTUtils。这个RESTUtils工具类是resteasy-link自己提供的,在项目里查找用到它的地方:

$ grep -rl 'RESTUtils' *
resteasy-links/src/main/java/org/jboss/resteasy/links/impl/LinkDecorator.java
resteasy-links/src/main/java/org/jboss/resteasy/links/impl/RESTUtils.java
$

可以看到是LinkDecorator在使用。下面是使用之处:

$ grep -rl 'LinkDecorator' *
resteasy-links/src/main/java/org/jboss/resteasy/links/impl/LinkDecorator.java
resteasy-links/src/main/java/org/jboss/resteasy/links/AddLinks.java
$

以下是LinkDecorator的具体实现:

public class LinkDecorator implements DecoratorProcessor<Marshaller, AddLinks> {

   public Marshaller decorate(Marshaller target, final AddLinks annotation,
         Class type, Annotation[] annotations, MediaType mediaType) {
      target.setListener(new Listener() {
         @Override
         public void beforeMarshal(Object entity) {
            UriInfo uriInfo = ResteasyContext.getContextData(UriInfo.class);
            ResourceMethodRegistry registry = (ResourceMethodRegistry) ResteasyContext.getContextData(Registry.class);

            // find all rest service classes and scan them
            RESTUtils.addDiscovery(entity, uriInfo, registry);
         }
      });
      return target;
   }
}

可以看到decorate方法的重点是往Marshaller target里面添加一个listener。而这个listener干的事情是把RESTUtils.addDiscovery(..)方法执行一下,注入ResourceMethodRegistry。这样,RESTUtils就可以在运行时工作了。下面这个是RESTUtils的类图:

回到 LinkDecorator,它是一个DecoratorProcessor,用于decorate的Marshaller,用户给需要decorate的class标注AddLinks标记。下面是AddLinks的源代码:

package org.jboss.resteasy.links;

import org.jboss.resteasy.annotations.Decorator;
import org.jboss.resteasy.links.impl.LinkDecorator;

import javax.xml.bind.Marshaller;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Use on any JAX-RS method if you want RESTEasy to inject the RESTServiceDiscovery
 * to every entity in the response. This will only inject RESTServiceDiscovery instances
 * on entities that have a field of this type, but it will be done recursively on the response's
 * entity.
 * @author <a href="mailto:stef@epardaud.fr">Stéphane Épardaud</a>
 */
@Target( { ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER,
      ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Decorator(processor = LinkDecorator.class, target = Marshaller.class)
@Documented
public @interface AddLinks {
}

查找@AddLinks使用到的地方:

$ grep -rl 'AddLinks' * | grep java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/BookStoreMinimal.java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/SecureBookStore.java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/SecureBookStoreMinimal.java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/el/BookStoreInvalidEL.java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/el/BookStoreNoPackage.java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/BookStore.java
resteasy-links/src/test/java/org/jboss/resteasy/links/test/IDServiceTest.java
resteasy-links/src/main/java/org/jboss/resteasy/links/impl/LinkDecorator.java
resteasy-links/src/main/java/org/jboss/resteasy/links/AddLinks.java
resteasy-links/src/main/java/org/jboss/resteasy/links/RESTServiceDiscovery.java

可以看到一些resteasy-links的tests使用到了@AddLinks功能并进行测试。后续分析这些测试就可以学习AddLinks的整套工作机制。