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,我们直接引入相关的包 ...
随机推荐
- create_workqueue和create_singlethread_workqueue【转】
本文转载自:http://bgutech.blog.163.com/blog/static/18261124320116181119889/ 1. 什么是workqueueLinux中的Workque ...
- Go CSP模型
CSP 是 Communicating Sequential Process 的简称,中文可以叫做通信顺序进程,是一种并发编程模型,由 Tony Hoare 于 1977 年提出.简单来说,CSP 模 ...
- 【bzoj1345】[Baltic2007]序列问题Sequence
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1345 因为合并的花费是较大数的权值,所以每个数肯定是和附近的小数合并完后才与大数合并, ...
- js用星号隐藏电话中间四位号码
$(document).ready(function(){ var mobile="{$user.mobile}"; var reg=/^(\d{3})\d{4}(\d{4})$/ ...
- 集群环境ssh免密码登录设置
一.准备工作 1) 用客户端工具(ssh client或者putty)连接到linux服务器.在root用户下输入命令 vi /etc/hosts,用vi编辑hosts文件,如下: #127.0.0. ...
- HTML中table边框的显示总结
一.1.显示表格的4个边框:<table border frame=box>2.只显示上边框: <table border frame=above>3.只显示下边框: < ...
- contenteditable支持度
contenteditable attribute (basic support) - Working Draft Global user stats*: Support: 86.71% Partia ...
- vue组件 Prop传递数据
组件实例的作用域是孤立的.这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据.要让子组件使用父组件的数据,我们需要通过子组件的props选项. prop 是单向绑定的:当父组件的属性变化时, ...
- boot小知识
lg 大, md 中等, sm 小, xs 极小. 可以单独用,也可以混合用,不同的屏幕用不同的比例. push ,pull 推拉.这个不实用. row里面可以嵌套实用row. 挤不下的时候,就会自动 ...
- js的事件处理与闭包:
var i = 0; for(i=0;i<5;i++){ (function(i){ setTimeout(function(){alert(i)},3000); })(i) } // 上面打印 ...