热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

详解SpringBoot自动装配的方法步骤

这篇文章主要介绍了详解SpringBoot自动装配的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在《Spring Boot Hello World》中介绍了一个简单的spring boot例子,体验了spring boot中的诸多特性,其中的自动配置特性极大的简化了程序开发中的工作(不用写一行XML)。本文我们就来看一下spring boot是如何做到自动配置的。
首先阐明,spring boot的自动配置是基于spring framework提供的特性实现的,所以在本文中,我们先介绍spring framework的相关特性,在了解了这些基础知识后,我们再来看spring boot的自动配置是如何实现的。

基于Java代码对Spring进行配置

在以往使用spring framework进行程序开发时,相信大家也只是使用XML搭配注解的方式对spring容器进行配置,例如在XML文件中使用指定spring需要扫描package的根路径。

除了使用XML对spring进行配置,还可以使用Java代码执行完全相同的配置。下面我们详细看一下如何使用Java代码对spring容器进行配置,详细内容可参考这里。

使用Java代码进行spring配置,有两个核心注解@Configuration和@Bean:

@Configuration
public class AppConfig {

  @Bean
  public SampleService sampleService() {
    return new SampleServiceImpl();
  }
}

@Bean注解用于修饰方法,方法的返回值会作为一个bean装载到spring容器中。bean的id就是方法的名字。

@Configuration注解用于修饰一个类,它表明这个类的作用是用来对spring容器进行配置的。

上面的Java代码相当于下面的XML配置:


  

使用AnnotationConfigApplicationContext类构建一个spring容器,从容器中取出对应的bean的测试代码如下:

public static void main(String[] args) {
  ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  SampleService myService = ctx.getBean("sampleService" ,SampleService.class);
  myService.doService();
}

Java代码配置ComponentScan

使用@ComponentScan注解指定需要扫描package的根路径:

@Configuration
@ComponentScan(basePackages = "com.**.service.impl")
public class AppConfig {

}

上面的Java代码相当于下面的XML配置:


  

此外,AnnotationConfigApplicationContext类还提供了scan方法用于指定要扫描的包路径。我们可以删除AppConfig类上的@ComponentScan注解,在构造spring容器时使用下面代码:

public static void main(String[] args) {
  AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  ctx.scan("com.**.service.impl");
  ctx.refresh();
  SampleService myService = ctx.getBean("sampleService" ,SampleService.class);
  myService.doService();
}

使用@Import组合多个配置

将所有的spring配置全部放在同一个类中肯定是不合适的,这会导致那个配置类非常复杂。通常会创建多个配置类,再借助@Import将多个配置类组合成一个。@Import的功能类似于XML中的

@Configuration
public class ConfigA {

  @Bean
  public A a() {
    return new A();
  }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

  @Bean
  public B b() {
    return new B();
  }
}

上面的代码分别创建了两个配置类ConfigA和ConfigB,它们分别定义了a和b两个Bean。在ConfigB上使用@Import注解导入ConfigA的配置,此时应用代码如果加载ConfigB的配置,就自动也加载了ConfigA的配置。如下代码所示:

public static void main(String[] args) {
  // 只加载ConfigB一个配置类,但同时也包含了ConfigA的配置
  ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

  A a = ctx.getBean(A.class);
  B b = ctx.getBean(B.class);

  System.out.println(a);
  System.out.println(b);
}

@Import还可以同时导入多个配置类。当有多个配置类需要同时导入时,示意代码如下:

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

  @Bean
  public DataSource dataSource() {
    // return new DataSource
  }
}

条件注解@Conditional

@Conditional注解根据某一个条件是否成立来判断是否构建Bean。借助Condition接口可以表示一个特定条件。例如下面代码实现了一个条件,当然这个条件始终成立:

public class SampleCondition implements Condition {

  @Override
  public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    // 如果条件成立返回true, 反之返回false
    return true;
  }
}

有了表达条件的类SampleCondition,接下来我们就可以通过@Conditional注解对创建bean的函数进行配置:

请输入代码@Configuration

public class ConditionConfig {

  // 只有当满足SampleCondition指定的条件时,参会构造id时sampleBean这个bean。当然这里的条件始终成立
  @Conditional(SampleCondition.class)
  @Bean
  public SampleBean sampleBean() {
    return new SampleBean();
  }
}

由于SampleCondition的matches方法返回true,表示创建bean的条件成立,所以sampleBean会被创建。如果matches返回false,sampleBean就不会被构建。

在spring boot中,根据这个原理提供了很多@ConditionOnXXX的注解,这些注解都在包org.springframework.boot.autoconfigure.condition下面。例如比较常见的@ConditionalOnClass注解,这个注解的判断逻辑是只有指定的某个类在classpath上存在时,判断条件才成立。@ConditionalOnClass的具体代码如下:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

  Class<&#63;>[] value() default {};

  String[] name() default {};
}

@ConditionalOnClass具体的判断逻辑可参看OnClassCondition类。

@SpringBootApplication注解

介绍完前面这些基础的知识后,我们来看Spring Boot是如何实现自动装配的。
《Spring Boot官方文档第14章》推荐在程序的main class上使用注解@SpringBootApplication对Spring应用进行自动配置,我们就从分析这个注解开始。下面是@SpringBootApplication主要代码:

@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 {
  // ...

@SpringBootApplication是一个组合注解,主要由@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan三个注解构成。

@SpringBootConfiguration表明被标注的类提供了Spring Boot应用的配置,其实这个注解与@Configuration注解的功能类似。
@ComponentScan指定需要扫描package的路径,@SpringBootApplication也提供了相应属性,指定需要扫描哪些package或不扫描哪些package。《Spring Boot官方文档》建议将应用的main class放置于整个工程的根路径,并用@SpringBootApplication注解修饰main class,这样整个项目的子package就都会被自动扫描包含。建议的工程结构如下所示,其中Application就是应用的main class。

com
 +- example
   +- myapplication
     +- Application.java
     |
     +- customer
     |  +- Customer.java
     |  +- CustomerController.java
     |  +- CustomerService.java
     |  +- CustomerRepository.java
     |
     +- order
       +- Order.java
       +- OrderController.java
       +- OrderService.java
       +- OrderRepository.java

@EnableAutoConfiguration是这里最重要的注解,它实现了对Spring Boot应用自动装配的功能。@EnableAutoConfiguration是利用SpringFactoriesLoader机制加载自动装配配置的,它的配置数据在META-INF/spring.factories中,我们打开spring-boot-autoconfigure jar中的该文件,发现EnableAutoConfiguration对应着N多XXXAutoConfiguration配置类,我们截取几个重要的配置类如下(已经删除了很多):

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoCOnfiguration=\
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.CloudServiceConnectorsAutoConfiguration,\
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.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
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.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
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.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.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,\

可以看到Spring Boot提供了N多XXXAutoConfiguration类,有Spring Framework的、Web的、redis的、JDBC的等等。
我们从其中选择HttpEncodingAutoConfiguration这个类来看下它是如何实现自动配置的:

@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
    matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

  private final HttpProperties.Encoding properties;

  public HttpEncodingAutoConfiguration(HttpProperties properties) {
    this.properties = properties.getEncoding();
  }

  @Bean
  @ConditionalOnMissingBean
  public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
    return filter;
  }

上面代码表示,只有在满足如下条件时,才会注入characterEncodingFilter这个bean:

  1. 只有在WebApplication的情况
  2. classpath上必须存在CharacterEncodingFilter类
  3. 配置文件中配置了spring.http.encoding.enabled为true或者没有配置
  4. Spring容器中不存在类型为CharacterEncodingFilter的bean

总结

Spring Boot自动装配的原理并不是非常复杂,其实背后的主要原理就是条件注解。

当我们使用@EnableAutoConfiguration注解激活自动装配时,实质对应着很多XXXAutoConfiguration类在执行装配工作,这些XXXAutoConfiguration类是在spring-boot-autoconfigure jar中的META-INF/spring.factories文件中配置好的,@EnableAutoConfiguration通过SpringFactoriesLoader机制创建XXXAutoConfiguration这些bean。XXXAutoConfiguration的bean会依次执行并判断是否需要创建对应的bean注入到Spring容器中。

在每个XXXAutoConfiguration类中,都会利用多种类型的条件注解@ConditionOnXXX对当前的应用环境做判断,如应用程序是否为Web应用、classpath路径上是否包含对应的类、Spring容器中是否已经包含了对应类型的bean。如果判断条件都成立,XXXAutoConfiguration就会认为需要向Spring容器中注入这个bean,否则就忽略。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 单点登录原理及实现方案详解
    本文详细介绍了单点登录的原理及实现方案,其中包括共享Session的方式,以及基于Redis的Session共享方案。同时,还分享了作者在应用环境中所遇到的问题和经验,希望对读者有所帮助。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • SpringMVC工作流程概述
    SpringMVC工作流程概述 ... [详细]
  • 一、Struts2是一个基于MVC设计模式的Web应用框架在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2优点1、实现 ... [详细]
  • springboot基于redis配置session共享项目环境配置pom.xml引入依赖application.properties配置Cookie序列化(高版本不需要)测试启 ... [详细]
  • 2018深入java目标计划及学习内容
    本文介绍了作者在2018年的深入java目标计划,包括学习计划和工作中要用到的内容。作者计划学习的内容包括kafka、zookeeper、hbase、hdoop、spark、elasticsearch、solr、spring cloud、mysql、mybatis等。其中,作者对jvm的学习有一定了解,并计划通读《jvm》一书。此外,作者还提到了《HotSpot实战》和《高性能MySQL》等书籍。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
  • ps:写的第一个,不足之处,欢迎拍砖---只是想用自己的方法一步步去实现一些框架看似高大上的小功能(比如说模型中的toArraytoJsonsetAtt ... [详细]
  • Django + Ansible 主机管理(有源码)
    本文给大家介绍如何利用DjangoAnsible进行Web项目管理。Django介绍一个可以使Web开发工作愉快并且高效的Web开发框架,能够以最小的代价构建和维护高 ... [详细]
  • 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储模式
    代码已上传Github+Gitee,文末有地址  书接上文:前几回文章中,我们花了三天的时间简单了解了下接口文档Swagger框架,已经完全解放了我们的以前的Word说明文档,并且可以在线进行调 ... [详细]
  • 我将SpringMVC升级到Spring3.2.5.我的一些剩余调用即使存在,也会返回无法识别的字段异常.这是错误.Resolvingexceptionfrom ... [详细]
  • 03Spring使用注解方式注入
    基于注解的DI注入1.导包环境搭建:导入aop包(spring-aop-4.1.6.RELEASE.jar)2.创建类3.创建spring.xml配置文件(必须在src目录下)该配 ... [详细]
  • Kylin 单节点安装
    软件环境Hadoop:2.7,3.1(sincev2.5)Hive:0.13-1.2.1HBase:1.1,2.0(sincev2.5)Spark(optional)2.3.0K ... [详细]
author-avatar
journeylis-1998_246
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有