Spring Boot自动装配

Spring Boot自动装配

题记

在日常的开发过程中Spring Boot自动装配的特性给我们开发减少了很多重复性的工作,具体时如果实现自动装配的呢,我们会在下一篇文章做详细的分析,以及用源码解析跟实战方式带大家了解Spring Boot “自动化”的原因,我们将透过现象去发现本质。

Spring Boot介绍:
Create stand-alone Spring applications 
-- 创建一个独立的Spring应用
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files) 
-- 嵌入式的web服务器Tomcat、Jetty、Undertow(无需部署 war 文件)
Provide opinionated 'starter' dependencies to simplify your build configuration
-- 提供建议的 "启动" 依赖关系, 以简化生成配置
Automatically configure Spring and 3rd party libraries whenever possible
-- 尽可能自动配置 spring 和第三方库
Provide production-ready features such as metrics, health checks and externalized configuration
-- 具备为生产准备的特性,如指标、运行状况检查和外部化配置
Absolutely no code generation and no requirement for XML configuration
-- 尽可能的无需代码生成并且无XML配置

话不多说,开始进入本次分享的主题,在Spring Boot@EnableAutoConfiguration的使用显得十分普遍,使用越多遇到的问题可能也就越多。为什么笔者会这样说呢,因为在程序开发的过程中,引入新技术一般会有两个原因:

第一:更便捷,能明显提高开发效率,否则一般的团队很少会主动尝试新事物。毕竟新的就意味着有风险
第二:对程序有显著地提升(此处是指可以是性能,也可以是健壮性,可用性,并发处理能力等等)

注解分析

注解名称 作用
@Component 基础注解(元注解),可被嵌套
@Configuration 声明定义Bean,嵌套了@Component
@Import 导入类(如AutoConfigurationImportSelector.class)
@ImportResource 导入文件("classpath:/com/acme/database-config.xml")
@AutoConfigureAfter 条件注解控制相对顺序(在条件注入之后注入),Order实现绝对顺序控制
@ConditionalOnClass 当类存在时装配
@ConditionalOnMissingClass 当类不存在时装配
@EnableAutoConfiguration 使用该注解实现自动装配
@SpringBootConfiguration 包含@Configuration,启动类的标注
@PropertySource properties文件加载
@ConditionalOnSystemProperty properties文件中存在满足条件的属性时装配
@EnableConfigurationProperties 是带有@ConfigurationProperties注解的类生效

在实际的使用中h还会有很多注解的定义与使用,在我们的日常开发中需要根据实际情况去选择使用。

自动装配

由于Spring Boot自动装配的特性,所以在jar引入后,我们会得到Spring容器为我们量身定做的实例,即我们所谓的Bean,这个过程中当然不可或缺的有RestTemplate,使用JDBC或者MyBatis等时会有JdbcTemplate生成。

//在Spring Boot中通过@EnableAutoConfiguration实现自动装配对象的选择
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
//具体选择器实现
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

该示例即为RestTemplate自动注入,包含条件注解:在HttpMessageConvertersAutoConfiguration自动注入之后;在RestTemplate.class存在时注入

@Configuration
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
@ConditionalOnClass(RestTemplate.class)
public class RestTemplateAutoConfiguration {
    ...
        @Bean
}

该示例即为JdbcTemplate自动注入,包含条件注解:在DataSourceAutoConfiguration自动注入之后;在JdbcTemplate.classDataSource.class并且DataSource为单例时,存在时注入

@Configuration
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
public class JdbcTemplateAutoConfiguration {
    @Bean
    @Bean
        @Primary
        @ConditionalOnMissingBean(JdbcOperations.class)
        public JdbcTemplate jdbcTemplate() {
            ...
        }
}

RestTemplateAutoConfigurationJdbcTemplateAutoConfiguration在spring-boot-autoconfiguration包下的META-INF/spring.factories中声明

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration

而后被AutoConfigurationImportSelector选择加载,该类实现了ImportSelector接口,具体加载的位置在invokeBeanFactoryPostProcessors(beanFactory);注册的时候,实现Bean的注册;而在Bean注册之前会有BeanClassResourceEnvironment等的属性注入;Ordered接口主要是确定加载的顺序。

public class AutoConfigurationImportSelector
    implements DeferredImportSelectorBeanClassLoaderAwareResourceLoaderAware,
BeanFactoryAwareEnvironmentAwareOrdered 
{
    ...
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List configurations = getCandidateConfigurations(annotationMetadata,
                                                                 attributes);
        configurations = removeDuplicates(configurations);
        Set exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }
}

如上所述可以实现,RestTemplateAutoConfigurationJdbcTemplateAutoConfiguration的自动注入得以实现。

可自动装配列表

# 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,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

实现自动装配

首先梳理自动装配的流程

定义HelloWorldAutoConfiguration

/**
 * @ClassName: HelloWorldAutoConfiguration
 * @Description: HelloWorld 自动装配
 * @Author: 尚先生
 * @CreateDate: 2019/1/28
 * @Version: 1.0
 */

@Configuration // Spring 模式注解装配
@EnableHelloWorld // Spring @Enable 模块装配
public class HelloWorldAutoConfiguration {
    @Bean
    public String helloWorld() // 方法名即 Bean 名称
        return "Hello,World sxs";
    }
}

配置注解标记类

/**
 * @ClassName: EnableHelloWorld
 * @Description: HelloWorld 自动装配注解
 * @Author: 尚先生
 * @CreateDate: 2019/1/28
 * @Version: 1.0
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}

配置META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sxs.springboot.configuration.HelloWorldAutoConfiguration

测试实现结果

/**
 * @ClassName: EnableHelloWorldBootstrap
 * @Description: HelloWorld 自动装配引导类(测试类)
 * @Author: 尚先生
 * @CreateDate: 2019/1/28
 * @Version: 1.0
 */

@EnableHelloWorld
public class EnableHelloWorldBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloWorldBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        // helloWorld Bean 是否存在
        String helloWorld = context.getBean("helloWorld", String.class);
        System.out.println("helloWorld Bean : " + helloWorld);
        // 关闭上下文
        context.close();
    }
}

测试结果

helloWorld Bean : Hello,World sxs

自动装配的过程

1. 通过各种注解实现了类与类之间的依赖关系,容器在启动的时候Application.run,会调用EnableAutoConfigurationImportSelector.class的selectImports方法(其实是其父类的方法)

2. selectImports方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用BeanConfiguration列表

3. loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的spring.factories),获取到所有的Spring相关的Bean的全限定名ClassName,大概120多个

4. selectImports方法继续调用filter(configurations, autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是
@ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean

5. 最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IOC环境当中

往期文章

博客地址:https://blog.csdn.net/shang_xs
微信公众号地址:http://mp.weixin.qq.com/mp/homepage?__biz=MzUxMzk4MDc1OQ==&hid=2&sn=c6c58c06f6f8403af27b6743648b3055&scene=18#wechat_redirect

你可能感兴趣的:(spring,boot)