spring boot提供了一系列的starter pom來簡化maven的依賴加載,例如spring-boot-starter-web、spring-boot-starter-test等。html
在咱們定製本身的starter前先了解一下必須知道的概念。java
@Enable*註釋並非新發明的註釋,早在Spring 3框架就引入了這些註釋,用這些註釋替代XML配置文件。不少Spring開發者都知道@EnableTransactionManagement註釋,它可以聲明事務管理;@EnableWebMvc註釋,它能啓用Spring MVC;以及@EnableScheduling註釋,它能夠初始化一個調度器。 這些註釋事實上都是簡單的配置,經過@Import註釋導入。react
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({ EnableAutoConfigurationImportSelector.class, AutoConfigurationPackages.Registrar.class }) public @interface EnableAutoConfiguration { /** * Exclude specific auto-configuration classes such that they will never be applied. */ Class<?>[] exclude() default {}; }
EnableAutoConfigurationImportSelector類使用了Spring Core包的SpringFactoriesLoader類的loadFactoryNamesof()方法。web
SpringFactoriesLoader會查詢META-INF/spring.factories文件中包含的JAR文件。 redis
當找到spring.factories文件後,SpringFactoriesLoader將查詢配置文件命名的屬性。在例子中,是org.springframework.boot.autoconfigure.EnableAutoConfiguration。 spring
讓咱們來看看spring-boot-autoconfigure JAR文件,它真的包含了一個spring.factories文件,內容以下:mongodb
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration
在這個文件中,能夠看到一系列Spring Boot自動配置的列表。下面咱們來看這些配置的細節,以MongoAutoConfiguration爲例:express
@Configuration @ConditionalOnClass(Mongo.class) @EnableConfigurationProperties(MongoProperties.class) public class MongoAutoConfiguration { @Autowired private MongoProperties properties; private Mongo mongo; @PreDestroy public void close() throws UnknownHostException { if (this.mongo != null) { this.mongo.close(); } } @Bean @ConditionalOnMissingBean public Mongo mongo() throws UnknownHostException { this.mongo = this.properties.createMongoClient(); return this.mongo; } }
這個類進行了簡單的Spring配置,聲明瞭MongoDB所需典型Bean。
這個類跟其它不少類同樣,重度依賴於Spring Boot註釋: apache
Spring Boot的強大之處在於使用了Spring 4框架的新特性:@Conditional註釋,此註釋使得只有在特定條件知足時才啓用一些配置。
在Spring Boot的org.springframework.boot.autoconfigure.condition包中說明了使用@Conditional註釋能給咱們帶來什麼,下面對這些註釋作一個概述:tomcat
以@ConditionalOnExpression註釋爲例,它容許在Spring的EL表達式中寫一個條件。
@Conditional(OnExpressionCondition.class) @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) public @interface ConditionalOnExpression { /** * The SpEL expression to evaluate. Expression should return {@code true} if the * condition passes or {@code false} if it fails. */ String value() default "true"; }
在這個類中,咱們想利用@Conditional註釋,條件在OnExpressionCondition類中定義:
public class OnExpressionCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // ... // we first get a handle on the EL context via the ConditionContext boolean result = (Boolean) resolver.evaluate(expression, expressionContext); // ... // here we create a message the user will see when debugging return new ConditionOutcome(result, message.toString()); } }
在最後,@Conditional經過簡單的布爾表達式(即ConditionOutcome方法)來決定。
spring.factories還提供了第二種可能性,即定義應用程序的初始化。這使得咱們能夠在應用程序載入前操縱Spring的應用程序上下文ApplicationContext。
特別是,能夠在上下文建立監聽器,使用ConfigurableApplicationContext類的addApplicationListener()方法。
AutoConfigurationReportLoggingInitializer監聽到系統事件時,好比上下文刷新或應用程序啓動故障之類的事件,Spring Boot能夠執行一些工做。這有助於咱們以調試模式啓動應用程序時建立自動配置的報告。
要以調試模式啓動應用程序,可使用-Ddebug標識,或者在application.properties文件這添加屬性debug= true。
Spring Boot的官方文檔(http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-troubleshoot-auto-configuration)有助於理解自動配置期間發生了什麼。當以調試模式運行時,Spring Boot會產生一個報告,以下:
Positive matches: ----------------- MessageSourceAutoConfiguration - @ConditionalOnMissingBean (types: org.springframework.context.MessageSource; SearchStrategy: all) found no beans (OnBeanCondition) JmxAutoConfiguration - @ConditionalOnClass classes found: org.springframework.jmx.export.MBeanExporter (OnClassCondition) - SpEL expression on org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration: ${spring.jmx.enabled:true} (OnExpressionCondition) - @ConditionalOnMissingBean (types: org.springframework.jmx.export.MBeanExporter; SearchStrategy: all) found no beans (OnBeanCondition) DispatcherServletAutoConfiguration - found web application StandardServletEnvironment (OnWebApplicationCondition) - @ConditionalOnClass classes found: org.springframework.web.servlet.DispatcherServlet (OnClassCondition) Negative matches: ----------------- DataSourceAutoConfiguration - required @ConditionalOnClass classes not found: org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType (OnClassCondition) DataSourceTransactionManagerAutoConfiguration - required @ConditionalOnClass classes not found: org.springframework.jdbc.core.JdbcTemplate,org.springframework.transaction.PlatformTransactionManager (OnClassCondition) MongoAutoConfiguration - required @ConditionalOnClass classes not found: com.mongodb.Mongo (OnClassCondition) FallbackWebSecurityAutoConfiguration - SpEL expression on org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration: !${security.basic.enabled:true} (OnExpressionCondition) SecurityAutoConfiguration - required @ConditionalOnClass classes not found: org.springframework.security.authentication.AuthenticationManager (OnClassCondition) EmbeddedServletContainerAutoConfiguration.EmbeddedJetty - required @ConditionalOnClass classes not found: org.eclipse.jetty.server.Server,org.eclipse.jetty.util.Loader (OnClassCondition) WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#localeResolver - @ConditionalOnMissingBean (types: org.springframework.web.servlet.LocaleResolver; SearchStrategy: all) found no beans (OnBeanCondition) - SpEL expression: '${spring.mvc.locale:}' != '' (OnExpressionCondition) WebSocketAutoConfiguration - required @ConditionalOnClass classes not found: org.springframework.web.socket.WebSocketHandler,org.apache.tomcat.websocket.server.WsSci (OnClassCondition)
對於每一個自動配置,能夠看到它啓動或失敗的緣由。
Spring Boot經過配置信息指出:特定配置項被選中的緣由、列出匹配到對應類的配置項(positive match)、不包括某個配置項的緣由(negative match)。
package com.aa.cloud.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ClassName: EnableDemo * @Description: * @author weiyb * @date 2017年9月20日 下午4:19:19 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface EnableDemo { }
package com.aa.cloud.service; /** * @ClassName: UserService * @Description: * @author weiyb * @date 2017年9月20日 下午4:08:51 */ public class DemoService { public void demoStarter() { System.out.println("hello world"); } }
package com.aa.cloud.auto.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.aa.cloud.annotation.EnableDemo; import com.aa.cloud.service.DemoService; /** * @ClassName: DemoAutoConfiguration * @Description: * @author weiyb * @date 2017年9月20日 下午4:09:07 */ @Configuration @ConditionalOnBean(annotation = EnableDemo.class)//條件判斷,只有在使用了@EnableUser時,纔會啓動 public class DemoAutoConfiguration { @Bean public DemoService dbCountRunner() { return new DemoService(); } }
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.aa.cloud.auto.config.DemoAutoConfiguration
package com.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.aa.cloud.annotation.EnableDemo; /** * @ClassName: ProviderApplication * @Description: * @author weiyb * @date 2017年9月20日 下午4:08:42 */ @SpringBootApplication @EnableDemo public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
package com.demo.starter.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.aa.cloud.service.DemoService; import com.cloud.ProviderApplication; /** * @ClassName: DemoServiceTest * @Description: * @author weiyb * @date 2017年9月20日 下午4:08:35 */ @RunWith(value = SpringJUnit4ClassRunner.class) @SpringBootTest(classes = ProviderApplication.class) public class DemoServiceTest { @Resource private DemoService userService; @Test public void demoStarter() { userService.demoStarter(); } }