VRaptor4 @Named Components in velocity 模板

VRaptor4 @Named Components in velocity template

我有一个 vraptor4 项目,我想使用 apache velocity 作为模板引擎。

所以我将 br.com.caelum.vraptor.view.DefaultPathResolver 专门化为

@Specializes
public class VelocityPathResolver extends DefaultPathResolver {

    @Inject
    protected VelocityPathResolver(FormatResolver resolver) {
        super(resolver);
    }

    protected String getPrefix() {
        return "/WEB-INF/vm/";
    }

    protected String getExtension() {
        return "vm";
    }
}

效果不错,但我的模板中不能有 @Named 个组件。

拥有

@SessionScoped
@Named("mycmp")
public class MyComponent implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name = "My Component";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

我不能在我的速度模板 (.vm) 中将其称为 ${mycmp.name},但如果我使用 .jsp 它工作正常。

为了解决这个问题,我将 br.com.caelum.vraptor.core.DefaultResult 专门化为

@Specializes
public class VelocityResult extends DefaultResult {

    private final MyComponent mycmp;

    @Inject
    public VelocityResult(HttpServletRequest request, Container container, ExceptionMapper exceptions, TypeNameExtractor extractor,
                      Messages messages, MyComponent mycmp) {

        super(request, container, exceptions, extractor, messages);
        this.mycmp = mycmp;
    }

    @PostConstruct
    public void init() {
        include("mycmp", mycmp);
    }
}

是否有更好的方法在速度模板中包含 @Named 个组件?

看起来 CDI 的 @Named 不适用于速度模板,但您可以实施一个 Interceptor 来为您完成这项工作。一个例子是:

@Intercepts
public class IncluderInterceptor {

    @Inject private MyComponent mycmp;

    @AfterCall public void after() {
        result.include("mycmp", mycmp);
        // any other bean you want to
    }
}

考虑一个更灵活的解决方案,您可以创建一个注释并使用它来定义应包含哪个 bean...类似这样的东西:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Included {
}

因此您可以在 类 处添加 @Included:

@Included public class MyComponent { ... }

并且只需在 IncluderInterceptor:

上添加 accept 方法
@Intercepts
public class IncluderInterceptor {

    @Inject @Any Instance<Object> allBeans;

    @AfterCall public void after() {
        // foreach allBeans, if has @Included, so
        // include bean.getClass().getSimpleName()
        // with first letter lower case, or something
    }
}

当然,如果您只包含几个 bean,那么第一个解决方案就足够了。最好的问候