在上一篇中,我们添加了对 Freemarker的支持,这里我们再添加多 Velocity 的支持,同时留出扩展接口。方便用户自己添加模板引擎。我们这里全试用注解,无需配置文件。这个功能也正好可以很好的体现出CDI 强大的扩展性。
我们定义一个接口来表示模板引擎的功能。
public interface ResponeseTemplateHandler { void init(String path) throws IOException; void process(RequestContext context) throws IOException; }
对于@ResponseTemplate注解,我们在使用在 Controller 的方法上面的时候通过其type的值来确定调用使用的引擎模板,value的值表示模板所在的路径。
@Qualifier @Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ResponseTemplate { String value() default ""; TemplateType type(); String customname() default ""; }
public enum TemplateType { FREEMARKER,VELOCITY,CUSTOM }
我们这里提供了 Freemarker 和 Velocity 的实现,分别使用@ResponseTemplate(type = TemplateType.FREEMARKER) 和 @ResponseTemplate(type = TemplateType.VELOCITY) 作为限定词。
@ResponseTemplate(type = TemplateType.FREEMARKER) public class FreeMarkerTemplate implements ResponeseTemplateHandler{ private Configuration cfg; public void init(String path) throws IOException { cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File(path)); } public void process(RequestContext context) throws IOException { init(context.getRequest().getSession().getServletContext().getRealPath("/")); String templatePath = context.getMethod().getResponseTemplate().value(); Template t = cfg.getTemplate(templatePath); try { HttpServletResponse response = context.getResponse(); response.setContentType("text/html"); t.process(context.getOutcome(), context.getResponse().getWriter()); } catch (TemplateException e) { e.printStackTrace(); } } }
@ResponseTemplate(type = TemplateType.VELOCITY) public class VelocityTemplate implements ResponeseTemplateHandler{ private VelocityEngine engine; public void init(String path) throws IOException { engine = new VelocityEngine(); engine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, path); } public void process(RequestContext context) throws IOException { init(context.getRequest().getSession().getServletContext().getRealPath("/")); String templatePath = context.getMethod().getResponseTemplate().value(); Template t = engine.getTemplate(templatePath); try { HttpServletResponse response = context.getResponse(); response.setContentType("text/html"); if(!(context.getOutcome() instanceof Map)){ throw new Exception("The return value of template method must be a map"); } VelocityContext velocityContext = new VelocityContext((Map)context.getOutcome()); t.merge(velocityContext, context.getResponse().getWriter()); } catch (TemplateException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
在Controller 方法执行完以后,我们根据它的 @ResponseTemplate 注解来注入对应的模板引擎。在CDI中,我们可以使用 BeanManager 实现 Bean的注入。利用它的 getBeans 和 getReference 方法。这里我们需要先定义出 @ResponseTemplate 注解的实现类。其实注解也是一个接口,注解这方面没有太深入的研究过,用起来的感觉是很爽的。
@ResponseTemplate的实现类 ResponseTemplateLiteral
public class ResponseTemplateLiteral extends AnnotationLiteral<ResponseTemplate> implements ResponseTemplate{ private static final long serialVersionUID = -3438074414725731211L; private TemplateType type; private String customname = ""; public ResponseTemplateLiteral(TemplateType type){ this.type = type; } public ResponseTemplateLiteral(TemplateType type,String customname){ this.type = type; this.customname = customname; } public String value() { return ""; } public TemplateType type() { return type; } public String customname() { return customname; } }我们写一个模板的默认处理类,Controller 方法的值返回后,我们就交给这个类处理。然后在这个类里面动态注入对应的模板引擎。
public class DefaultTemplateHandler { @Inject private BeanManager beanManager; public void handle(RequestContext context) { try { ResponseTemplate template = context.getMethod() .getResponseTemplate(); ResponseTemplateLiteral templateLiteral ; TemplateType type = template.type(); if (type.equals(TemplateType.CUSTOM) && (template.customname().equals(""))) { throw new MvcdiTemplateException("Custom template name Can not be empty with TemplateType.CUSTOM"); } if(type.equals(TemplateType.CUSTOM)){ templateLiteral = new ResponseTemplateLiteral(type, template.customname()); }else{ templateLiteral = new ResponseTemplateLiteral(type); } Bean<?> bean = beanManager .getBeans(ResponeseTemplateHandler.class,templateLiteral).iterator() .next(); ResponeseTemplateHandler templateHandler = (ResponeseTemplateHandler) beanManager .getReference(bean, ResponeseTemplateHandler.class, beanManager.createCreationalContext(bean)); templateHandler.process(context); } catch (IOException e) { e.printStackTrace(); } catch (MvcdiTemplateException e) { e.printStackTrace(); } } }
在上面代码中 ,我们通过 new 出不同的 ResponseTemplate 的实现类,然后通过 BeanManager 注入ResponeseTemplateHandler 接口不同的实现类。
假如,我们需要使用 Smarty4j 模板引擎。那么我们只需要实现ResponeseTemplateHandler接口,然后给实现类添加注解 @ResponseTemplate(type= TemplateType.CUSTOM, customname ="Smarty4j"), 然后在使用的Controller 的方法上面也加上这个注解就可以了。