Spring Boot 自动配置@Enable的秘密

Spring Boot项目中,我们只需要引入spring-boot-start-web就能使用web开发相关的功能,而不需要添加任何配置。其实Spring Boot帮我们做好了很多的自动配置,在应用启动的时候依托于@EnableAutoConfiguration注解来激活自动配置的模块。本文中的@Enable@Conditional我并没有细说,这两部分内容在我之前的文章中有详细的阐述。如果读者并不这两部分内容熟悉的话,请先读我的另外的两篇文章。之前的文章中探究了@Enable***用来激活指定的配置模块和使用@Conditional实现条件配置,在这个基础上再来看这个这个自动配置就非常简单了,我们一起去看一看。

演示环境

  • IntelliJ IDEA 2018.2.1 (Community Edition)
  • Maven 3.5.4
  • Spring Boot 2.1.1.RELEASE

走进源码

这里我们就以Web模块为例,来说明一下这个自动装配的怎么实现的。正常情况下我们的启动类上都会标注@SpringBootApplication,在这个@SpringBootApplication注解里面我们会发现@EnableAutoConfiguration用来激活我们的自动配置模块。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

而这个@EnableAutoConfiguration注解里面使用了***ImportSelector,用来实现对自动配置类的筛选。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

而这个AutoConfigurationImportSelector这个类,又借助了工厂加载机制,根据@EnableAutoConfiguration注解的全类名去加载META-INF/spring.factories文件中配置好的自动配置的模块。

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
            
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
	// 省略其它方法.......            
}

spring-boot-autoconfigure-2.1.1.RELEASE包下的META-INF/spring.factories文件:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\

上面的代码,一些不是我们叙述重点的都被我省略了。

然后去加载这个WebMvcAutoConfiguration的配置:

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}

首先这个WebMvcAutoConfiguration类是一个被@Configuration的配置类;然后它被标注了很多@ConditionalOn***的条件配置的条件,只有都满足条件了才会被容器装载;接着还定义了一些加载的顺序,以及在哪些配置的类之后加载。到这儿我们的web模块就被装载完成了。

实现自动配置

根据上面我们分析好的流程,我们可以自定义实现我们的自动配置。流程如下:

  1. 定义一个***AutoConfiguration的一个自动配置的类。
  2. 使用@Enable***,用来激活我们自定义的某些配置模块。
  3. 使用@ConditionalOn***,来根据自定义条件灵活的加载我们的 配置类。
  4. 利用工厂加载机制,将我们的配置类配到META-INF/spring.factories文件中去。
  5. 使用@EnableAutoConfiguration激活自动配置,验证我们的实现。

1、定义一个HelloAutoConfiguration

我们首先编写HelloAutoConfiguration的自动配置类:

/**
 * Hello模块自动装配
 *
 * @author Jerome Zhu
 */
@Configuration // 模式注解
@EnableHello // Enable Hello模块装配  ↑
@ConditionalOnSystemProperty(key = "user.name", value = "user") // 条件装配 ↑
public class HelloAutoConfiguration {

    @Bean
    public String sayHello() {
        return "hello jerome, this is auto-configure.";
    }
}

这里使用@Configuration表明这是一个配置类,使用我们@EnableHello用来激活我们自定义的Hello模块。使用@ConditionalOnSystemProperty用来判断系统参数是否满足我们制定的参数。

我的当前电脑的user.name的值为user

2、使用@Enable***,激活模块配置

之前文章中编写好的@EnableHello注解:

/**
 * 激活Hello模块配置
 *
 * @author Jerome Zhu
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloConfiguration.class) // 模块装配:注解驱动实现
public @interface EnableHello {
}

以及HelloConfiguration配置类:

/**
 * Hello模块的配置
 *
 * @author Jerome Zhu
 */
@Configuration
public class HelloConfiguration {

    @Bean
    public String hello() { // method key is bean key
        System.out.println("Bean : hello is loading.");
        return "hello word !";
    }

}

这里就激活了我们的自定义的Hello模块,会向容器中添加一个名为hellojavaBean

3、使用@ConditionalOn***,判断自定义条件

之前文章中编写好的@ConditionalOnSystemProperty注解:

/**
 * 根据系统环境条件判断是否装配Bean
 *
 * @author Jerome Zhu
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {

    /**
     * system property key
     */
    String key();

    /**
     * system property key
     */
    String value();
}

以及OnSystemPropertyCondition类:

/**
 * {@link Condition} 实现匹配系统配置的条件
 *
 * @author Jerome Zhu
 */
public class OnSystemPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());

        String propertyKey = String.valueOf(attributes.get("key"));

        String propertyValue = String.valueOf(attributes.get("value"));

        // 获取系统变量 propertyKey 对应的值
        String sysPropertyValue = System.getProperty(propertyKey);

        return propertyValue.equals(sysPropertyValue);
    }
}

这里的条件配置就是判断当前系统变量中的属性,是否与我们限制的值相同,相同则向容器中注册。

4、添加自动配置类到META-INF/spring.factories文件中。

在我们的项目的resources文件夹下,新建META-INF文件夹,在该文件夹下新建spring.factories并添加一下配置:

# 添加自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xin.jerome.autoconfigure.configuration.HelloAutoConfiguration

5、验证自定义自动配置

我们编写一个启动类EnableHelloAutoConfigurationBootstrap来验证我们的配置是否生效。

/**
 * 使用spring boot 的自动装配
 *      {@link EnableAutoConfiguration} -> MATA-INF/spring.factories 取到相应的配置
 *
 * @author Jerome Zhu
 */
@EnableAutoConfiguration
public class EnableHelloAutoConfigurationBootstrap {
    public static void main(String[] args) {

        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloAutoConfigurationBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        String helloBean = context.getBean("hello", String.class);
        System.out.println("hello Bean: " + helloBean);
        String sayHelloBean = context.getBean("sayHello", String.class);
        System.out.println("sayHello Bean: " + sayHelloBean);
        context.close();
    }
}

首先使用@EnableAutoConfiguration激活自动配置模块,因为我们的@Enable ->HelloConfiguration注册了一个名为hellojavaBean,我们的HelloAutoConfiguration配置注册了一个名为sayHellojavaBean。我们根据上下文去获取我们的javaBean,并输出其内容。

当我们运行过后,控制台会有如下输出:

hello Bean: hello word !
sayHello Bean: hello jerome, this is auto-configure.

说明我们的自定义自动装配模块成功了。

总结

其实自动装配模块的有这么几个要点,一是使用@Enable***模块用来激活我们的某一个功能模块;二是通过@Conditional***根据条件灵活的绝对是否要注册该组件,三是添加我们配置好的自动配置类到spring.factories文件中,让Spring Boot提供的自动配置模块去读取并加载它。

具体的代码可以参考GitHub:spring-boot-demo-autoconfigure小节。

原文地址:Spring Boot 自动配置@Enable的秘密

转载于:https://my.oschina.net/JeromeZhu/blog/3004097

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Pro Spring Boot 2: An Authoritative Guide to Building Microservices, Web and Enterprise Applications, and Best Practices Quickly and productively develop complex Spring applications and microservices out of the box, with minimal concern over things like configurations. This revised book will show you how to fully leverage the Spring Boot 2 technology and how to apply it to create enterprise ready applications that just work. It will also cover what's been added to the new Spring Boot 2 release, including Spring Framework 5 features like WebFlux, Security, Actuator and the new way to expose Metrics through Micrometer framework, and more. This book is your authoritative hands-on practical guide for increasing your enterprise Java and cloud application productivity while decreasing development time. It's a no nonsense guide with case studies of increasing complexity throughout the book. The author, a senior solutions architect and Principal Technical instructor with Pivotal, the company behind the Spring Framework, shares his experience, insights and first-hand knowledge about how Spring Boot technology works and best practices. Pro Spring Boot 2 is an essential book for your Spring learning and reference library. What You Will Learn Configure and use Spring Boot Use non-functional requirements with Spring Boot Actuator Carry out web development with Spring Boot Persistence with JDBC, JPA and NoSQL Databases Messaging with JMS, RabbitMQ and WebSockets Test and deploy with Spring Boot A quick look at the Spring Cloud projects Microservices and deployment to the Cloud Extend Spring Boot by creating your own Spring Boot Starter and @Enable feature Who This Book Is For Experienced Spring and Java developers seeking increased productivity gains and decreased complexity and development time in their applications and software services.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值