当前位置: 移动技术网 > IT编程>开发语言>Java > 详解Spring Boot自动装配的方法步骤

详解Spring Boot自动装配的方法步骤

2019年07月19日  | 移动技术网IT编程  | 我要评论

像火花像蝴蝶演员表,潘映竹,吉尔尼斯双虹

在《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文件中使用<context:component-scan base-package="**"/>指定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配置:

<beans>
  <bean id="sampleservice" class="com.**.sampleserviceimpl"/>
</beans>

使用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配置:

<beans>
  <context:component-scan base-package="com.**.service.impl"/>
</beans>

此外,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中的<import/>。

@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<?>[] 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,否则就忽略。

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网