SpringBoot自动化配置之三:深入SpringBoot:自定义EnableAutoConfiguration
前言
上面几篇文章介绍了SpringFramework的一些原理,这里开始介绍一下SpringBoot,并通过自定义一些功能来介绍SpringBoot的原理。SpringBoot在SpringFramework的基础上集成了Web容器,日志等功能,可以快速的实现Web服务。
先看SpringBoot必要的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
spring-boot包含了SpringBoot的核心实现类。
spring-boot-autoconfigure则是根据当前classpath的类来注入不同的实现类。
EnableAutoConfiguration
EnableAutoConfiguration源码:
package org.springframework.boot.autoconfigure; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {}; }
观察EnableAutoConfiguration可以发现,这里Import了EnableAutoConfigurationImportSelector。
Import主要是配合Configuration来使用的,用来导出更多的Configuration类,ConfigurationClassPostProcessor会读取Import的内容来实现具体的逻辑。
EnableAutoConfigurationImportSelector分析:
EnableAutoConfigurationImportSelector实现了DeferredImportSelector接口,并实现了selectImports方法,用来导出指定配置Configuration类(在META-INF/spring.factories配置)。
@Order(Ordered.LOWEST_PRECEDENCE - 1) public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware { private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; @Override public String[] selectImports(AnnotationMetadata metadata) { try { AnnotationAttributes attributes = getAttributes(metadata); List<String> configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } } ... }
导出的类是通过SpringFactoriesLoader.loadFactoryNames()读取了ClassPath下面的META-INF/spring.factories文件。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { return SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); }
META-INF/spring.factories文件,这个文件内容大致如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ ... org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration ...
可以看继续往里看源码:
package org.springframework.core.io.support; ... public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; ... public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } } ... }
EmbeddedServletContainerAutoConfiguration是实现web服务的主要的配置类,EmbeddedServletContainerAutoConfiguration这个类会根据当前存在的类的信息注入必要的EmbeddedServletContainerFactory类。
package org.springframework.boot.autoconfigure.web; @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { /** * Nested configuration for if Tomcat is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { return new JettyEmbeddedServletContainerFactory(); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { return new UndertowEmbeddedServletContainerFactory(); } } //... }
spring-boot-starter-tomcat引入了tomcat的依赖,所以EmbeddedServletContainerAutoConfiguration发现存在Tomcat.class就会注入TomcatEmbeddedServletContainerFactory来内置web容器。
示例1:
先看一个基本的例子,这个例子实现了一个简单的web服务。
引入必要的依赖,开启web服务需要引入对应的EmbedWeb容器。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency>
程序入口,这里使用了EnableAutoConfiguration注解。
package com.dxz.autoconfig; import java.util.HashMap; import java.util.Map; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Configuration @EnableAutoConfiguration public class Bootstrap { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Bootstrap.class); springApplication.run(args); } @Controller public static class MyController { @RequestMapping @ResponseBody public Map index() { Map<String, String> map = new HashMap<String, String>(); map.put("hello", "world"); return map; } } }
结果:
三、自定义EnableAutoConfiguration示例
1、自定义EnableAutoConfiguration,这里Import了MyEnableAutoConfigurationImport。
package com.dxz.autoconfig; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.autoconfigure.AutoConfigurationPackage; import org.springframework.context.annotation.Import; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(MyEnableAutoConfigurationImport.class) public @interface MyEnableAutoConfiguration { }
2、自定义EnableAutoConfigurationImport,注入了ClassLoader,并调用SpringFactoriesLoader.loadFactoryNames()方法,导出Configuration的类。
package com.dxz.autoconfig; import java.util.List; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.DeferredImportSelector; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.type.AnnotationMetadata; public class MyEnableAutoConfigurationImport implements DeferredImportSelector, BeanClassLoaderAware { private ClassLoader classLoader; public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public String[] selectImports(AnnotationMetadata importingClassMetadata) { List<String> beanNames = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader); return beanNames.toArray(new String[beanNames.size()]); } }
3、入口类,这里使用了MyEnableAutoConfiguration注解。
package com.dxz.autoconfig; import java.util.HashMap; import java.util.Map; import org.springframework.boot.SpringApplication; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Configuration @MyEnableAutoConfiguration public class CustomizeEnableAutoConfigure { public static void main(String[] args) { SpringApplication application = new SpringApplication(CustomizeEnableAutoConfigure.class); application.run(args); } @Controller public static class MyController { @RequestMapping @ResponseBody public Map index() { Map<String, String> map = new HashMap<String, String>(); map.put("hello", "world2"); return map; } } }
结果:
最新文章
- RxAndroid/java小记
- android SDK下载及中文API地址
- SQLServer(MSSQL)、MySQL、SQLite、Access相互迁移转换工具 DB2DB v1.2
- [译]git fetch
- Tomcat 和 Resin 比较,哪个更适合你?
- nodejs 基本类型和语法
- C语言基础:指针类型与指针和数组、字符串的关系
- 打开已存在 Android项目及常见的问题
- 手把手教你在openshift上搭建wordpress博客(二)
- Spark:使用Spark Shell的两个示例
- Git常用功能记录
- laravel的时间日期处理包Carbon用法
- 小tips:JS严格模式(use strict)下不能使用arguments.callee的替代方案
- Centos 安装 Nginx 详细过程
- 构建高性能J2EE应用的五种核心策略
- USB2.0相关应用笔记集锦
- Hystrix使用说明,配置参数说明
- Base64编码后通过Url传值
- [转]SVN 乱码问题
- Error: listen EACCES 0.0.0.0:8080 错误解决记录