前言

上面几篇文章介绍了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,这里ImportMyEnableAutoConfigurationImport

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的更多相关文章

  1. SpringBoot自动化配置之四:SpringBoot 之Starter(自动配置)、Command-line runners

    Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳这里 Starter ...

  2. SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理

    springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...

  3. SpringBoot自动化配置之二:自动配置(AutoConfigure)原理、EnableAutoConfiguration、condition

    自动配置绝对算得上是Spring Boot的最大亮点,完美的展示了CoC约定优于配置: Spring Boot能自动配置Spring各种子项目(Spring MVC, Spring Security, ...

  4. 让SpringBoot自动化配置不再神秘

    本文若有任何纰漏.错误,还请不吝指出! 注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory.关于BeanFactory, ...

  5. SpringBoot(八):SpringBoot中配置字符编码 Springboot中文乱码处理

    SpringBoot中配置字符编码一共有两种方式 方式一: 使用传统的Spring提供的字符编码过滤器(和第二种比较,此方式复杂,由于时间原因这里先不介绍了,后续补上) 方式二(推荐使用) 在appl ...

  6. SpringBoot自动化配置的注解开关原理

    我们以一个最简单的例子来完成这个需求:定义一个注解EnableContentService,使用了这个注解的程序会自动注入ContentService这个bean. @Retention(Retent ...

  7. SpringBoot自动化配置之四:@Conditional注解详解

    前言 之前在分析spring boot 源码时导出可见@ConditionalOnBean 之类的注解,那么它到底是如何使用的以及其工作流程如何,我们这里就围绕以下几点来分析: @Conditiona ...

  8. springboot Aop配置,并使用自定义注解annotation,并且拦截service层

    前言 用Spring Boot的AOP来简化处理自定义注解,并将通过实现一个简单的方法执行判断节点是否开始的状态示列源码. AOP概念 面向侧面的程序设计(aspect-oriented progra ...

  9. springboot笔记-1.自动化配置的关键

    最近发现看过的东西容易忘,但是写一遍之后印象倒是会深刻的多. 总所周知springboot极大的简化了java开发繁琐性,而其最大的优势应该就是自动化配置了.比如要使用redis,我们直接引入相关的包 ...

随机推荐

  1. Windows系统 本地文件如何复制到远程服务器

    很多人在使用远程服务器的时候往往要将本地的文件传输到远程服务器内,方法有很多种,下面介绍下如何使用Windows自带的远程桌面连接程序将文件复制到远程服务器内. 1.首先,点击windows开始按钮, ...

  2. centos iscsi 配置

    首先是服务器的设置:[root@localhost 桌面]# yum install scsi-target-*  -y     安装服务  配置yum的方法太简单了,我就不写了[root@local ...

  3. c#.NET中日志信息写入Windows日志中解决方案

    1. 目的应用系统的开发和维护离不开日志系统,选择一个功能强大的日志系统解决方案是应用系统开发过程中很重要的一部分.在.net环境下的日志系统解决方案有许多种,log4net是其中的佼佼者.在Wind ...

  4. jQuery颜色面板插件

    /** * jQuery颜色面板插件 * * 使用方法:input框的id默认为inputObj,button框的id默认为btnObj,也可以自定义aaa,bbb * 1.初始化颜色面板:$.col ...

  5. DataWarehouse- 从面试定位自己的水平

    1.讲一下什么是维度表和事实表.用户资料表算是什么类型表. 2. 维度建模属于第几范式,让你对维度建模改进,有什么思路吗. 3. 了解数据血缘分析吗,让你实现的话有什么技术方案,感觉难点在哪. 4. ...

  6. Angular各版本和组件下载

    Angular各版本和组件下载:https://code.angularjs.org/

  7. ie 元素兼容性总结

    css 属性元素 2.z-index 正常按自身层级决定显示顺序,在ie6 7 还需要依赖于父级的层级决定,排布在后边的元素排在前面.后来者居上覆盖前者. IE6,7支持inline元素转换成inli ...

  8. Redis简介 & 与Memcache的区别

    redis 是一个基于内存的高性能key-value数据库.   Reids的特点 Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操 ...

  9. html5 frameset5内嵌框架集

    利用html5 frameset内嵌框架简单做一个网页,网页的布局大体如下: 我们可以将其分为四个部分: 第一部分:top图片栏 第二部分:left链接栏 第三部分:right内容栏 第四部分:网页整 ...

  10. poj2135最小费用流

    裸题,就是存个模板 最小费用流是用spfa求解的,目的是方便求解负环,spfa类似于最大流中的bfs过程 #include<map> #include<set> #includ ...