JEE6 CDI 扩展实现 MVC (四) 实现多模板引擎支持,并提供扩展接口

        在上一篇中,我们添加了对 Freemarker的支持,这里我们再添加多 Velocity 的支持,同时留出扩展接口。方便用户自己添加模板引擎。我们这里全试用注解,无需配置文件。这个功能也正好可以很好的体现出CDI 强大的扩展性。

        1. 定义模板处理的接口

        我们定义一个接口来表示模板引擎的功能。

public interface ResponeseTemplateHandler {

	void init(String path) throws IOException;
	
	void process(RequestContext context) throws IOException;
}

        2. 定义注解和枚举

        对于@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
}

        3.实现接口并通过限定词区分

        我们这里提供了 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();
		}
	}

}

        4. 动态注入接口的实现类

        在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 的方法上面也加上这个注解就可以了。



你可能感兴趣的:(mvc,CDI,JEE6)