104. Spring Boot 启动流程分析第三篇【从零开始学Spring Boot】

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

问题的提出:Spring Boot不像spring需要定义xml文件文件,那么spring boot是如何在没有配置文件的情况下为我们启动一个完整的WEB工程的呢。我们先@SpringBootApplication开始分析下这个启动流程,源代码取自:1.4.1.RELEASE。

我们先看下@SpringBootApplication的代码:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))

public @interface SpringBootApplication {}

可以看到这里是一个复合注解,如:@Target,@Document,@Retention是我们常见的注解,这里重点说下@SpringBootConfiguration注解:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Configuration

public @interface SpringBootConfiguration {}

这个注解重要的就是@Configuration:这是标注当前类为Java Config类。

在@SpringBootApplication中另外一个重要的注解就是@EnableAutoConfiguration:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(EnableAutoConfigurationImportSelector.class)

public @interface EnableAutoConfiguration {}

这个注解上@Import(EnableAutoConfigurationImportSelector.class)代表引入其它的Spring的JavaConfig接着进入EnableAutoConfigurationImportSelector.class。

关注一下以下的方法:

@Override

public String[] selectImports(AnnotationMetadata metadata) {

if (!isEnabled(metadata)) {

returnNO_IMPORTS;

}

try {

AnnotationAttributes attributes = getAttributes(metadata);

List configurations = getCandidateConfigurations(metadata,

attributes);

configurations = removeDuplicates(configurations);

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

}

}

进入:List configurations = getCandidateConfigurations(metadata,attributes);

protected List getCandidateConfigurations(AnnotationMetadata metadata,

AnnotationAttributes attributes) {

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

}

在进入:List configurations = SpringFactoriesLoader.loadFactoryNames(

getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

代码如下:

public static List loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {

String factoryClassName = factoryClass.getName();

try {

Enumeration urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

List result = new ArrayList();

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

}

}

在上面的代码可以看到自动配置器会跟根据传入的factoryClass.getName()到spring.factories的文件中找到相应的key,从而加载里面的类但我们打开spring-boot-autoconfigure-1.4.1.RELEASE.jar里面的spring.factories可以发现很多key。查看spring.factories文件:

# Initializers

org.springframework.context.ApplicationContextInitializer=\

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\

org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# Application Listeners

org.springframework.context.ApplicationListener=\

org.springframework.boot.autoconfigure.BackgroundPreinitializer

# 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.MessageSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\

这里以org.springframework.boot.autoconfigure.data.Redis.RedisAutoConfiguration为例查看代码如下:

@Configuration

@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })

@EnableConfigurationProperties

public class RedisAutoConfiguration {

@Bean(name = “org.springframework.autoconfigure.redis.RedisProperties”)

@ConditionalOnMissingBean

public RedisProperties redisProperties() {

return new RedisProperties();

}

/**

* Redis connection configuration.

*/

@Configuration

@ConditionalOnMissingClass(“org.apache.commons.pool2.impl.GenericObjectPool”)

protected static class RedisConnectionConfiguration

extends AbstractRedisConfiguration {

@Bean

@ConditionalOnMissingBean(RedisConnectionFactory.class)

public JedisConnectionFactory redisConnectionFactory()

throws UnknownHostException {

return applyProperties(new JedisConnectionFactory(getSentinelConfig()));

}

}

//省略部分代码…

/**

* Standard Redis configuration.

*/

@Configuration

protected static class RedisConfiguration {

@Bean

@ConditionalOnMissingBean(name = “redisTemplate”)

public RedisTemplate<Object, Object> redisTemplate(

RedisConnectionFactory redisConnectionFactory)

throws UnknownHostException {

RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();

template.setConnectionFactory(redisConnectionFactory);

return template;

}

}

}

把类简化一下基本上就可以看出这就是一个Spring的注解版的配置

(1)@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })这个注解的意思是:当存在JedisConnection.class, RedisOperations.class, Jedis.class三个类时才解析RedisAutoConfiguration配置类,否则不解析这一个配置类

(2)@ConditionalOnMissingBean(name = “redisTemplate”)这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行

(3)内部代码可以看出里面又定义了两个带@Configuration注解的配置类,这两个配置类会向SpringIOC容器注入可能3个bean:

首先当类路径下存在(GenericObjectPool.class)时则注入JedisConnectionFactory 的实例如果Spring容器中不存在name = “redisTemplate”的实体,则创建RedisTemplate和StringRedisTemplate实例注入容器,这样在Spring的项目中,就可以用在任意的Spring管理的bean中注册用RedisTemplate和StringRedisTemplate的实例来对redis进入操作了。

通过以上分析的过程我们可以发现只要一个基于SpringBoot项目的类路径下存在JedisConnection.class, RedisOperations.class, Jedis.class就可以触发自动化配置,意思说我们只要在maven的项目中依赖了spring-data-redis-1.7.2.RELEASE.jar和C:jedis-2.8.2.jar就可以触发自动配置,但这样不是每集成一个功能都要去分析里其自动化配置类,那就代不到开箱即用的效果了。所以Spring-boot为我提供了统一的starter可以直接配置好相关触发自动配置的所有的类的依赖集如redis的start如下:

org.springframework.boot spring-boot-starter-data-redis

因为maven依赖的传递性,我们只要依赖starter就可以看在类路径下配置好所有的触发自动配置的所有类,实现开箱即用的功能。


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » 104. Spring Boot 启动流程分析第三篇【从零开始学Spring Boot】

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏