spring boot 自定義starter

    spring boot提供了一系列的starter pom來簡化maven的依賴加載,例如spring-boot-starter-web、spring-boot-starter-test等。html

    在咱們定製本身的starter前先了解一下必須知道的概念。java

一、@Enable*註釋

    @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

  • @ConditionOnClass激活一個配置,在類路徑中只能存在一到幾個這樣的類。 
  • @EnableConfigurationProperties自動映射一個POJO到Spring Boot配置文件(默認是application.properties文件)的屬性集。 
  • @ConditionalOnMissingBean啓用一個Bean定義,但必須是這個Bean以前未定義過纔有效。 
    還可使用@ AutoConfigureBefore註釋、@AutoConfigureAfter註釋來定義這些配置類的載入順序。

二、@Conditional註釋

    Spring Boot的強大之處在於使用了Spring 4框架的新特性:@Conditional註釋,此註釋使得只有在特定條件知足時才啓用一些配置。 
    在Spring Boot的org.springframework.boot.autoconfigure.condition包中說明了使用@Conditional註釋能給咱們帶來什麼,下面對這些註釋作一個概述:tomcat

  • @ConditionalOnBean
  • @ConditionalOnClass
  • @ConditionalOnExpression
  • @ConditionalOnMissingBean
  • @ConditionalOnMissingClass
  • @ConditionalOnNotWebApplication
  • @ConditionalOnResource
  • @ConditionalOnWebApplication

    以@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自動配置

 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)。

-------------demo-----------

五、建立starter項目

5.1 建立一個enable註解

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 {

}

5.2 服務類

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");
	}
}

5.3 自動配置類

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();
	}
}

5.4 配置META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.aa.cloud.auto.config.DemoAutoConfiguration

六、建立demo starter test項目

6.1 啓動類

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);
	}
}

6.2 測試類

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();
	}
}
相關文章
相關標籤/搜索