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;
}
}
}
结果:

SpringBoot自动化配置之三:深入SpringBoot:自定义EnableAutoConfiguration的更多相关文章
- SpringBoot自动化配置之四:SpringBoot 之Starter(自动配置)、Command-line runners
Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳这里 Starter ...
- SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理
springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...
- SpringBoot自动化配置之二:自动配置(AutoConfigure)原理、EnableAutoConfiguration、condition
自动配置绝对算得上是Spring Boot的最大亮点,完美的展示了CoC约定优于配置: Spring Boot能自动配置Spring各种子项目(Spring MVC, Spring Security, ...
- 让SpringBoot自动化配置不再神秘
本文若有任何纰漏.错误,还请不吝指出! 注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory.关于BeanFactory, ...
- SpringBoot(八):SpringBoot中配置字符编码 Springboot中文乱码处理
SpringBoot中配置字符编码一共有两种方式 方式一: 使用传统的Spring提供的字符编码过滤器(和第二种比较,此方式复杂,由于时间原因这里先不介绍了,后续补上) 方式二(推荐使用) 在appl ...
- SpringBoot自动化配置的注解开关原理
我们以一个最简单的例子来完成这个需求:定义一个注解EnableContentService,使用了这个注解的程序会自动注入ContentService这个bean. @Retention(Retent ...
- SpringBoot自动化配置之四:@Conditional注解详解
前言 之前在分析spring boot 源码时导出可见@ConditionalOnBean 之类的注解,那么它到底是如何使用的以及其工作流程如何,我们这里就围绕以下几点来分析: @Conditiona ...
- springboot Aop配置,并使用自定义注解annotation,并且拦截service层
前言 用Spring Boot的AOP来简化处理自定义注解,并将通过实现一个简单的方法执行判断节点是否开始的状态示列源码. AOP概念 面向侧面的程序设计(aspect-oriented progra ...
- springboot笔记-1.自动化配置的关键
最近发现看过的东西容易忘,但是写一遍之后印象倒是会深刻的多. 总所周知springboot极大的简化了java开发繁琐性,而其最大的优势应该就是自动化配置了.比如要使用redis,我们直接引入相关的包 ...
随机推荐
- [RK3288][Android6.0] 调试笔记 --- user版本默认显示开发者选项【转】
本文转载自:https://blog.csdn.net/kris_fei/article/details/70157137 Platform: ROCKCHIPOS: Android 6.0Kerne ...
- 【转】服务器.htaccess 详解以及 .htaccess 参数说明
htaccess文件(或者”分布式配置文件”)提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录.作为用户,所能使用的命令受到限 ...
- 树分治learning
学习了树的点分治,树的边分治似乎因为复杂度过高而并不出众,于是没学 自己总结了一下 有些时候面对一些树上的结构 并且解决的是和路径有关的问题的时候 如果是多个询问 关注点在每次给出两个点,求一些关于这 ...
- ajax01简介
(Asynchronous JavaScript and XML)Ajax :异步 JavaScript 和 XML,一种允许浏览器和服务器通信进行少量数据交换而无需重新加载整个网页,以实现更新部分网 ...
- RowSet
import java.io.FileInputStream; import java.util.Properties; import javax.sql.rowset.JdbcRowSet; imp ...
- Announcing the Release of ASP.NET MVC 5.1, ASP.NET Web API 2.1 and ASP.NET Web Pages 3.1 for VS2012
The NuGet packages for ASP.NET MVC 5.1, ASP.NET Web API 2.1 and ASP.NET Web Pages 3.1 are now live o ...
- 搭建Rsync服务器
部署Rsync服务器需要创建至少 一个配置文件,默认在系统中并不存在Rsync配置文件,对于服务器而言,配置文件创建完成后,使用守护进程模式启动rsync程序即可. 使用Centos 系统安装 部署R ...
- JavaScript--收藏栏添加按钮,放大hdu题目字体
觉得HDOJ的题目字体太小了,一波小操作 在收藏栏添加:添加网页->网址改为: javascript: void((function() { var element = document.get ...
- MySQL多种安装方式选择
1.rpm包安装方式 rpm包的安装方式非常简单,这里以el6平台下的mysql-5.6.34版本为例,首先,要通过上述搜狐镜像地址下载到如下四个MySQL相关软件安装包. a.下载安装包 MySQL ...
- session 丢失问题
1. 存到memcached中, 十分简单, 在使用session之前, 加入下面两行代码 int_set('session.save_handler', 'memcache'); int_set(' ...