在 Spring Boot 项目中,怎样将 pom.xml 文件里面添加的依赖中的 bean 注册到 Spring Boot 项目的 Spring 容器中呢?
你可能会首先想到使用 @ComponentScan 注解,遗憾的是 @ComponentScan 注解只能扫描 Spring Boot 项目包内的 bean 并注册到 Spring 容器中,项目依赖包中的 bean 不会被扫描和注册。此时,我们需要使用 @EnableAutoConfiguration 注解来注册项目依赖包中的 bean。而 spring.factories 文件,可用来记录项目包外需要注册的 bean 类名。
使用 spring.factories 文件有什么好处呢?假如我们封装了一个插件,该插件提供给其他开发人员使用。我们可以在 spring.factories 文件中指定需要自动注册到 Spring 容器的 bean 和一些配置信息。使用该插件的开发人员只需少许配置,甚至不进行任何配置也能正常使用。
我们以 spring-cloud-netflix-eureka-server 为例,该依赖用来实现 Eureka server 服务,它的 jar 包结构如下:
打开 spring.factories 文件,该文件内容如下:
该文件仅有一个 EnableAutoConfiguration 配置信息,而 EnableAutoConfiguration 用来启用 Spring Application Context 的自动配置,尝试猜测和配置您可能需要的 bean。通常根据您的类路径和定义的 bean 来应用自动配置类。上面配置文件将自动执行 EurekaServerAutoConfiguration 类,然后注册到 Spring 容器。源码如下:
@Configuration( proxyBeanMethods = false ) @Import({EurekaServerInitializerConfiguration.class}) @ConditionalOnBean({Marker.class}) @EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class}) @PropertySource({"classpath:/eureka/server.properties"}) public class EurekaServerAutoConfiguration implements WebMvcConfigurer { private static final String[] EUREKA_PACKAGES = new String[]{"com.netflix.discovery", "com.netflix.eureka"}; @Autowired private ApplicationInfoManager applicationInfoManager; //... }
我们将演示 spring.factories 文件的用法,以及介绍该文件中可以配置那些 Bean。内容如下:
## Initializers org.springframework.context.ApplicationContextInitializer=\ com.huangx.springboot.autoconfig.MyApplicationContextInitializer ## Application Listeners org.springframework.context.ApplicationListener=\ com.huangx.springboot.autoconfig.MyApplicationListener ## Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ com.huangx.springboot.autoconfig.MyAutoConfigurationImportListener ## Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ com.huangx.springboot.autoconfig.MyConfigurationCondition ## Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.huangx.springboot.autoconfig.MyConfiguration ## Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ com.huangx.springboot.autoconfig.MyFailureAnalyzer ## Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ com.huangx.springboot.autoconfig.MyTemplateAvailabilityProvider
下面将分别介绍各种配置的具体含义:
该配置项用来配置实现了 ApplicationContextInitializer 接口的类,这些类用来实现上下文初始化。配置如下:
org.springframework.context.ApplicationContextInitializer=\ com.huangx.springboot.autoconfig.MyApplicationContextInitializer
实例代码:
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("MyApplicationContextInitializer.initialize() " + applicationContext); } }
配置应用程序监听器,该监听器必须实现 ApplicationListener 接口。它可以用来监听 ApplicationEvent 事件。配置如下:
org.springframework.context.ApplicationListener=\ com.huangx.springboot.autoconfig.MyApplicationListener
实例代码:
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println("MyApplicationListener.onApplicationEvent() " + event); if(event instanceof ApplicationStartedEvent) { throw new RuntimeException("我故意抛出的错误,仅仅为了触发自定义 MyFailureAnalyzer"); } } }
该配置项用来配置自动配置导入监听器,监听器必须实现 AutoConfigurationImportListener 接口。该监听器可以监听 AutoConfigurationImportEvent 事件。配置如下:
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ com.huangx.springboot.autoconfig.MyAutoConfigurationImportListener
实例代码:
public class MyAutoConfigurationImportListener implements AutoConfigurationImportListener { @Override public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { System.out.println("MyAutoConfigurationImportListener.onAutoConfigurationImportEvent() " + event); } }
配置自动配置导入过滤器,过滤器必须实现 AutoConfigurationImportFilter 接口。该过滤器用来过滤那些自动配置类可用,配置信息:
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ com.huangx.springboot.autoconfig.MyConfigurationCondition
实例代码:
public class MyConfigurationCondition implements AutoConfigurationImportFilter { @Override public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { System.out.println("MyConfigurationCondition.match() autoConfigurationClasses=" + Arrays.toString(autoConfigurationClasses) + ", autoConfigurationMetadata=" + autoConfigurationMetadata); return new boolean[0]; } }
配置自动配置类。这些配置类需要添加 @Configuration 注解,配置如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.huangx.springboot.autoconfig.MyConfiguration
实例代码:
@Configuration public class MyConfiguration { public MyConfiguration() { System.out.println("MyConfiguration()"); } }
配置自定的错误分析类,该分析器需要实现 FailureAnalyzer 接口。配置信息:
org.springframework.boot.diagnostics.FailureAnalyzer=\ com.huangx.springboot.autoconfig.MyFailureAnalyzer
实例代码:
/** * 自定义自己的错误分析器 FailureAnalyzer * @author Administrator 2021/4/1 13:14 * @version 1.0 */ public class MyFailureAnalyzer implements FailureAnalyzer { @Override public FailureAnalysis analyze(Throwable failure) { System.out.println("MyFailureAnalyzer.analyze() failure=" + failure); return new FailureAnalysis("MyFailureAnalyzer execute", "test spring.factories", failure); } }
运行效果如下图:
配置模板的可用性提供者,提供者需要实现 TemplateAvailabilityProvider 接口,配置如下:
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ com.huangx.springboot.autoconfig.MyTemplateAvailabilityProvider
实例代码:
/** * 验证指定的模板是否支持 * @author Administrator 2021/4/1 13:22 * @version 1.0 */ public class MyTemplateAvailabilityProvider implements TemplateAvailabilityProvider { @Override public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader, ResourceLoader resourceLoader) { System.out.println("MyTemplateAvailabilityProvider.isTemplateAvailable() view=" + view + ", environment=" + environment + ", classLoader=" + classLoader + "resourceLoader=" + resourceLoader); return false; } }