spring源码分析之freemarker整合
2021-03-13 10:28
标签:ble tput cached getview info other not vat potential 1.定义(准备工作) freemarker整合需要定义FreeMarkerViewResolver 在web.xml定义: 注意:上面的contextclass定义在FrameworkServlet中,contextclass设置了一个自定义的context类,且必须是WebApplicationContext的实现。 When using the default FrameworkServlet implementation,
* the context class must also implement the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface.
* @see #createWebApplicationContext
*/
public void setContextClass(Class> contextClass) {
this.contextClass = contextClass;
} 可以看出,在dispatcherServlet时定义了bean: FreeMarkerViewResolver、 If no ViewResolver beans are defined in the BeanFactory for this
* namespace, we default to InternalResourceViewResolver.
*/
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
Map 然后FreeMarkerViewResolver设置FreeMarkerView FreeMarkerView在初始化时查找 Checks that the template for the default Locale can be found:
* FreeMarker will check non-Locale-specific templates if a
* locale-specific one is not found.
* @see freemarker.cache.TemplateCache#getTemplate
*/
@Override
protected void initServletContext(ServletContext servletContext) throws BeansException {
if (getConfiguration() != null) {
this.taglibFactory = new TaglibFactory(servletContext);
}
else {
FreeMarkerConfig config = autodetectConfiguration();
setConfiguration(config.getConfiguration());
this.taglibFactory = config.getTaglibFactory();
}
GenericServlet servlet = new GenericServletAdapter();
try {
servlet.init(new DelegatingServletConfig());
}
catch (ServletException ex) {
throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex);
}
this.servletContextHashModel = new ServletContextHashModel(servlet, getObjectWrapper());
} 自动检测 DispatcherServlet开始 This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there‘s a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name ‘" + mv.getViewName() +
"‘ in servlet with name ‘" + getServletName() + "‘");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name ‘" + getServletName() + "‘");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name ‘" + getServletName() + "‘");
}
try {
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name ‘" +
getServletName() + "‘", ex);
}
throw ex;
}
}
2.1 创建视图View 如红色1所示,调用DispatcherServlet的 resolveViewName方法 The default implementations asks all ViewResolvers of this dispatcher.
* Can be overridden for custom resolution strategies, potentially based on
* specific model attributes or request parameters.
* @param viewName the name of the view to resolve
* @param model the model to be passed to the view
* @param locale the current locale
* @param request current HTTP servlet request
* @return the View object, or {@code null} if none found
* @throws Exception if the view cannot be resolved
* (typically in case of problems creating an actual View object)
* @see ViewResolver#resolveViewName
*/
protected View resolveViewName(String viewName, Map 然后调用各种的ReviewResolver来解析视图AbstractCachingViewResolver 调用子类UrlBasedViewResolver来创建view对象 Not possible in {@code loadView}, since overridden
* {@code loadView} versions in subclasses might rely on the
* superclass always creating instances of the required view class.
* @see #loadView
* @see #requiredViewClass
*/
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
} 若前缀是redirect:或者forward:则跳入相应的逻辑进行处理,否则使用父逻辑 The default implementation delegates to {@link #loadView}.
* This can be overridden to resolve certain view names in a special fashion,
* before delegating to the actual {@code loadView} implementation
* provided by the subclass.
* @param viewName the name of the view to retrieve
* @param locale the Locale to retrieve the view for
* @return the View instance, or {@code null} if not found
* (optional, to allow for ViewResolver chaining)
* @throws Exception if the view couldn‘t be resolved
* @see #loadView
*/
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
/**
* Delegates to {@code buildView} for creating a new instance of the
* specified view class, and applies the following Spring lifecycle methods
* (as supported by the generic Spring bean factory):
* 2.2 渲染视图 如DispatchServlet红色部分2所示,调用View的render方法 void render(Map 具体实现由AbstractView来做 调用子类AbstractTemplateView实现上述红色部分 再调用子类FreeMarkerView实现 This method can be overridden if custom behavior is needed.
*/
@Override
protected void renderMergedTemplateModel(
Map 然后调用doRender方法 The default implementation renders the template specified by the "url"
* bean property, retrieved via {@code getTemplate}. It delegates to the
* {@code processTemplate} method to merge the template instance with
* the given template model.
* Adds the standard Freemarker hash models to the model: request parameters,
* request, session and application (ServletContext), as well as the JSP tag
* library hash model.
* Can be overridden to customize the behavior, for example to render
* multiple templates into a single view.
* @param model the model to use for rendering
* @param request current HTTP request
* @param response current servlet response
* @throws IOException if the template file could not be retrieved
* @throws Exception if rendering failed
* @see #setUrl
* @see org.springframework.web.servlet.support.RequestContextUtils#getLocale
* @see #getTemplate(java.util.Locale)
* @see #processTemplate
* @see freemarker.ext.servlet.FreemarkerServlet
*/
protected void doRender(Map 继续处理模板 Can be overridden to customize the behavior.
* @param template the template to process
* @param model the model for the template
* @param response servlet response (use this to get the OutputStream or Writer)
* @throws IOException if the template file could not be retrieved
* @throws TemplateException if thrown by FreeMarker
* @see freemarker.template.Template#process(Object, java.io.Writer)
*/
protected void processTemplate(Template template, SimpleHash model, HttpServletResponse response)
throws IOException, TemplateException {
template.process(model, response.getWriter());
} 调用freemarker jar中的freemarker.template.Template类的process方法 此过程超出spring的范围,故略去不述。 1.spring和freemarker的整合,需要定义两个bean:FreeMarkerViewResolver、FreeMarkerConfigurer。 2.spring在Dispatcher中定义了视图渲染的过程:创建视图,然后利用Freemarker本身提供的Template方法来处理。 处理过程中以Mode、request、response为参数。 参考文献 【1】http://baike.baidu.com/link?url=ETR7FFS21YwRaoIkWHYpJWl1rorsLrF3xgha7HepmiIRlrXwJ3ed7ZWBGgiCXF0fr1yezCCFzQomnSAU4tzljK 【2】https://github.com/JVerstry/Web-Related-Examples/tree/master/Spring-FreeMarker-Integration spring源码分析之freemarker整合 标签:ble tput cached getview info other not vat potential 原文地址:https://blog.51cto.com/15015181/2556830
FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据。package com.jverstry.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.jverstry")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver getViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache(false);
// resolver.setPrefix("");
resolver.setSuffix(".ftl");
return resolver;
}
@Bean
public FreeMarkerConfigurer getFreemarkerConfig() {
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setTemplateLoaderPath("WEB-INF/pages/");
return result;
}
}
/**
* Set a custom context class. This class must be of type
* {@link org.springframework.web.context.WebApplicationContext}.
*
FreeMarkerConfigurer
那么在DispatcherServlet中是如何识别的呢?/**
* Initialize the ViewResolvers used by this class.
*
public FreeMarkerViewResolver() {
setViewClass(requiredViewClass());
}
/**
* Requires {@link FreeMarkerView}.
*/
@Override
protected Class> requiredViewClass() {
return FreeMarkerView.class;
}
FreeMarkerConfigurer 的bean/**
* Invoked on startup. Looks for a single FreeMarkerConfig bean to
* find the relevant Configuration for this factory.
*
/**
* Autodetect a {@link FreeMarkerConfig} object via the ApplicationContext.
* @return the Configuration instance to use for FreeMarkerViews
* @throws BeansException if no Configuration instance could be found
* @see #getApplicationContext
* @see #setConfiguration
*/
protected FreeMarkerConfig autodetectConfiguration() throws BeansException {
try {
return BeanFactoryUtils.beanOfTypeIncludingAncestors(
getApplicationContext(), FreeMarkerConfig.class, true, false);
}
catch (NoSuchBeanDefinitionException ex) {
throw new ApplicationContextException(
"Must define a single FreeMarkerConfig bean in this web application context " +
"(may be inherited): FreeMarkerConfigurer is the usual implementation. " +
"This bean may be given any name.", ex);
}
}
/**
* Render the given ModelAndView.
*
/**
* Resolve the given view name into a View object (to be rendered).
*
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
/**
* Overridden to implement check for "redirect:" prefix.
*
/**
* Create the actual View object.
*
*
* @param viewName the name of the view to retrieve
* @return the View instance
* @throws Exception if the view couldn‘t be resolved
* @see #buildView(String)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
/**
* Prepares the view given the specified model, merging it with static
* attributes and a RequestContext attribute, if necessary.
* Delegates to renderMergedOutputModel for the actual rendering.
* @see #renderMergedOutputModel
*/
@Override
public void render(Map
@Override
protected final void renderMergedOutputModel(
Map
/**
* Process the model map by merging it with the FreeMarker template.
* Output is directed to the servlet response.
*
/**
* Render the FreeMarker view to the given response, using the given model
* map which contains the complete template model to use.
*
/**
* Process the FreeMarker template to the servlet response.
*
文章标题:spring源码分析之freemarker整合
文章链接:http://soscw.com/index.php/essay/64077.html