3.4 FreeMarkerAutoConfiguration实例分析

本节通过FreeMarkerAutoConfiguration实例来分析Spring Boot中集成Freemarker模板引擎的整个自动配置的过程。

3.4.1 spring-boot-starter-freemarker工程

spring-boot-starter-freemarker工程是实现Free Marker模板引擎自动配置的启动工程,其目录结构如下:

        .
        ├── pom.xml
        ├── spring-boot-starter-freemarker.iml
        └── src
            └── main
                └── resources
                    └── META-INF
                            └── spring.provides
          4 directories, 3 files

我们可以看出,这个工程没有任何Java代码,只有两个文件:pom.xml与spring.provides,其中,spring.provides文件如下:

        provides: freemarker, spring-context-support

主要是给这个starter起个好区分的名字。

Spring Boot通过starter对项目的依赖进行统一管理.starter利用了Maven的传递依赖解析机制,把常用库聚合在一起,组成了针对特定功能而定制的依赖starter。

我们可以使用IDEA提供的Maven依赖图分析的功能,得到spring-boot-starter-freemarker依赖的module,如图3-4所示。

图3-4 spring-boot-starter-freemarker依赖的module

从图中我们可以看出其中的依赖关系。

当Spring Boot Application中自动配置EnableAutoConfiguration的相关类执行完毕之后,Spring Boot会进一步解析对应类的配置信息。如果我们配置了spring-boot-starter-freemarker, Maven就会通过这个starter所依赖的spring-boot-autoconfigure,自动传递到spring-boot-autoconfigure工程中。

3.4.2 spring-boot-autoconfigure工程

我们来简单分析一下spring-boot-autoconfigure工程中关于FreeMarker自动配置的逻辑实现。FreeMarker自动配置的入口类是:

        org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration

这个配置类中导入了FreeMarkerServletWebConfiguration、FreeMarkerReactiveWebCon-figuration、FreeMarkerNonWebConfiguration配置类:

        @Configuration
        @ConditionalOnClass({ freemarker.template.Configuration.class,
                FreeMarkerConfigurationFactory.class })
        @EnableConfigurationProperties(FreeMarkerProperties.class)
        @Import({ FreeMarkerServletWebConfiguration.class,
                FreeMarkerReactiveWebConfiguration.class, FreeMarkerNonWebConfigura
                        tion.class })
            public class FreeMarkerAutoConfiguration

其中:

❑@Configuration是org.springframework.context.annotation包里面的注解。我们已经知道用@Configuration注解该类,等价于XML中配置Bean;用@Bean标注方法等价于XML中配置Bean。

❑@ConditionalOnClass是org.springframework.boot.autoconfigure.condition包里面的注解。意思是当类路径下有指定的类的条件下,才会去注册被标注的类为一个Bean。在上面的代码中的意思就是,当类路径中有:

            freemarker.template.Configuration.class
            FreeMarkerConfigurationFactory.class

这两个类的时候,才会去配置FreeMarkerAutoConfiguration。

❑@EnableConfigurationProperties,表示启动对FreeMarkerProperties.class的内嵌配置支持,自动将FreeMarkerProperties注册为一个Bean。这个FreeMarkerProperties类里面就是关于FreeMarker属性的配置:

        @ConfigurationProperties(prefix = "spring.freemarker")
        public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {
            public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/
                templates/";
            public static final String DEFAULT_PREFIX = "";
            public static final String DEFAULT_SUFFIX = ".ftl";
            …
        }

接下来,我们来看一下FreeMarkerServletWebConfiguration这个类。该类主要是用于配置基于servlet web context的Freemarker的配置。这个类的代码如下:

        @Configuration
        @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
        @ConditionalOnClass({ Servlet.class, FreeMarkerConfigurer.class })
        @AutoConfigureAfter(WebMvcAutoConfiguration.class)
        class FreeMarkerServletWebConfiguration extends AbstractFreeMarkerConfiguration {
            protected FreeMarkerServletWebConfiguration(FreeMarkerProperties pro
                perties) {
                super(properties);
            }
            @Bean
            @ConditionalOnMissingBean(FreeMarkerConfig.class)
            public FreeMarkerConfigurer freeMarkerConfigurer() {
                  FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
                  applyProperties(configurer);
                  return configurer;
              }
              @Bean
              public freemarker.template.Configuration freeMarkerConfiguration(
                      FreeMarkerConfig configurer) {
                  return configurer.getConfiguration();
              }
              @Bean
              @ConditionalOnMissingBean(name = "freeMarkerViewResolver")
              @ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMis
                  sing = true)
              public FreeMarkerViewResolver freeMarkerViewResolver() {
                  FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
                  getProperties().applyToMvcViewResolver(resolver);
                  return resolver;
              }
              @Bean
              @ConditionalOnMissingBean
              @ConditionalOnEnabledResourceChain
              public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
                  return new ResourceUrlEncodingFilter();
              }
          }

其中:

1)当该应用是基于Servlet的Web应用时,Spring容器内有Servlet.class、FreeMarker Configurer.class类实例存在。

2)Spring容器中不存在freeMarkerViewResolver的Bean。

3)应用程序的属性配置文件中没有匹配到"spring.freemarker.enabled"。

当1)2)3)三个条件都满足,则初始化freeMarkerViewResolver这个Bean。

我们也可以自定义自己的starter,以及实现对应的@MyEnableAutoConfiguration。Spring Boot有很多第三方starter,其自动配置的原理基本都是这样,比如mybatis-spring-boot-starter的MybatisAutoConfiguration,源码在如下地址:https://github.com/mybatis/spring-boot-starter,阅读源码可加深理解。