像火花像蝴蝶演员表,潘映竹,吉尔尼斯双虹
在《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:
总结
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,否则就忽略。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
浅析我对 String、StringBuilder、StringBuffer 的理解
使用IDEA搭建SSM框架的详细教程(spring + springMVC +MyBatis)
Springboot整合freemarker 404问题解决方案
引入mybatis-plus报 Invalid bound statement错误问题的解决方法
网友评论