spring boot自动配置

目录

 

自动配置原理

自定义自动配置


自动配置原理

我们跟随代码来看

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ServletComponentScan
@EnableScheduling
public class DaseApplication {
	public static void main(String[] args) {
		 SpringApplication.run(DaseApplication.class, args);
	}
}

这个入口类要使用@SpringBootApplication注解声明,它是SpringBoot的核心注解

@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,这么直白的名字,一看就知道它要开启自动配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

可以看到,在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能(@Enable*里面都是@import)而EnableAutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		try {
			AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
					.loadMetadata(this.beanClassLoader);
			AnnotationAttributes attributes = getAttributes(annotationMetadata);

            //1.扫描具有META-INF/spring.factories文件的jar包
			List<String> configurations = getCandidateConfigurations(annotationMetadata,
					attributes);
            //2.去重
			configurations = removeDuplicates(configurations);
			configurations = sort(configurations, autoConfigurationMetadata);

            //3.删除需要排除的类
			Set<String> exclusions = getExclusions(annotationMetadata, attributes);
			checkExcludedClasses(configurations, exclusions);
			configurations.removeAll(exclusions);
			configurations = filter(configurations, autoConfigurationMetadata);
			fireAutoConfigurationImportEvents(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

getCandidateConfigurations代码

//加载spring.factories实现

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;
	}

可以看到通过 SpringFactoriesLoader.loadFactoryNames()把 spring-boot-autoconfigure.jar/META-INF/spring.factories中每一个xxxAutoConfiguration文件都加载到容器中。进入SpringFactoriesLoader.loadFactoryNames()可以看到

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));

任何一个springboot应用,都会引入spring-boot-autoconfigure,而spring.factories文件就在该包下面。spring.factories文件是Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

上面的EnableAutoConfiguration配置了多个类,这些都是Spring Boot中的自动配置相关类;在启动过程中会解析对应类配置信息。每个Configuation类都定义了相关bean的实例化配置。都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来。如果我们自定义了一个starter的话,也要在该starter的jar包中提供 spring.factories文件,并且为其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类。所有框架的自动配置流程基本都是一样的,判断是否引入框架,获取配置参数,根据配置参数初始化框架相应组件。下面的初始化数据源的部分源码:

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {

    private static final Log logger = LogFactory
            .getLog(DataSourceAutoConfiguration.class);

    @Bean
    @ConditionalOnMissingBean
    public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
            ApplicationContext applicationContext) {
        return new DataSourceInitializer(properties, applicationContext);
    }
    //略
}

@Configuration:这个配置就不用多做解释了,我们一直在使用
@EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。
以下为SpringBoot内置条件注解:
@ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件
@ConditionalOnClass:当SpringIoc容器内存在指定Class的条件
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在时查找指定的位置
@ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件
@ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnSingleCandidate:当指定Bean在SpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件
以上注解都是元注解@Conditional演变而来的,根据不用的条件对应创建以上的具体条件注解。


自定义自动配置

 

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

1、新建服务类TestService

package com.bm.demostarter;

public class TestService {

    private String myip;

    private String myport;

    public String getMyip() {
        return myip;
    }

    public void setMyip(String myip) {
        this.myip = myip;
    }

    public String getMyport() {
        return myport;
    }

    public void setMyport(String myport) {
        this.myport = myport;
    }



    public TestService(String myip, String myport) {
        this.myip = myip;
        this.myport = myport;
    }

    public void printInfo() {
        System.out.println("打印:" + myip + " " + myport);
    }
}

2、创建属性配置实体类TestServiceProperties

package com.bm.demostarter;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix="test.config")
public class TestServiceProperties {
    String ip;
    String port;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }
}

 

3、新建一个自动配置类TestServiceAutoConfiguration

package com.bm.demostarter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(TestServiceProperties.class)
@ConditionalOnClass(TestService.class) //判断TestService这个类在类路径中是否存在
//test.config.enabled=true时才能启动,允许不存在该配置
@ConditionalOnProperty(name = "test.config.enabled", havingValue = "true", matchIfMissing = true)

public class TestServiceAutoConfiguration {


    @Autowired
    private TestServiceProperties testServiceProperties;

    @Bean(name = "testService")
    @ConditionalOnMissingBean(TestService.class)
//当容器中没有这个Bean时(BambooServer)就自动配置这个Bean,Bean的参数来自于BambooServerProperties
    public TestService mistraService() {
        TestService testService = new TestService(testServiceProperties.getIp(), testServiceProperties.getPort());
        return testService;
    }
}

 

4、自定义一个注解@EnableTestService

package com.bm.demostarter;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(TestServiceAutoConfiguration.class)
public @interface EnabledTestService {
}

5、resources - META-INF-spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bm.demostarter.TestServiceAutoConfiguration

打包生成jar

 

然后另一个项目要应用

        <dependency>
            <groupId>com.bm</groupId>
            <artifactId>demostarter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

写配置信息

 

引入@EnabledTestService

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ServletComponentScan
@EnableScheduling
@EnabledTestService
public class DaseApplication {

	/**
	* Title: main 
	* Description: 程序启动入口
	* @param args   
	* void
	 */
	public static void main(String[] args) {
		 SpringApplication.run(DaseApplication.class, args);
	}
}

调用

    @Autowired
	private TestService testService;

	@GetMapping("/ii")
	public String ii()
	{
		testService.printInfo();
		return "123123";
	}

效果

http://localhost:81/ii

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值