|
致力于最高效的Java學(xué)習(xí)
1. SpringBoot 源碼常用注解拾遺組合注解
@Value@Component public class Person { @Value('i am name') private String name; }
@Value(${key}) 的方式獲取全局配置文件中的指定配置項。@ConfigurationProperties@Value 的方式去配置項需要一個一個去取,這就顯得有點 low 了。我們可以使用 @ConfigurationProperties。@ConfigurationProperties 的類的所有屬性和配置文件中相關(guān)的配置項進行綁定。(默認從全局配置文件中獲取配置值),綁定之后我們就可以通過這個類去訪問全局配置文件中的屬性值了。person.name=kundy person.age=13 person.sex=male
@ConfigurationProperties 有一個 prefix 參數(shù),主要是用來指定該配置項在配置文件中的前綴。@Import
1. 直接導(dǎo)入普通的 Java 類public class Circle { public void sayHi() { System.out.println('Circle sayHi()'); } }
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class); Circle circle = context.getBean(Circle.class); circle.sayHi(); }
2. 配合自定義的 ImportSelector 使用public class Triangle { public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{'annotation.importannotation.waytwo.Triangle'}; } }
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigTwo.class); Circle circle = context.getBean(Circle.class); Triangle triangle = context.getBean(Triangle.class); circle.sayHi(); triangle.sayHi(); }
3. 配合 ImportBeanDefinitionRegistrar 使用public class Rectangle { public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rectangle.class); // 注冊一個名字叫做 rectangle 的 bean beanDefinitionRegistry.registerBeanDefinition('rectangle', rootBeanDefinition); } }
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigThree.class); Circle circle = context.getBean(Circle.class); Triangle triangle = context.getBean(Triangle.class); Rectangle rectangle = context.getBean(Rectangle.class); circle.sayHi(); triangle.sayHi(); rectangle.sayHi(); }
@Conditional
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return true; } }
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class); ConditionBean conditionBean = context.getBean(ConditionBean.class); conditionBean.sayHi(); }2. SpringBoot 啟動過程
SpringApplication.run() -> run(new Class[]{primarySource}, args) -> new SpringApplication(primarySources)).run(args)。new SpringApplication(primarySources)).run(args) 這個方法。
創(chuàng)建 SpringApplication 對象
運行 run() 方法public ConfigurableApplicationContext run(String... args) { // 創(chuàng)建計時器 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 聲明 IOC 容器 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); // 從類路徑下找到 META/INF/Spring.factories 獲取 SpringApplicationRunListeners SpringApplicationRunListeners listeners = this.getRunListeners(args); // 回調(diào)所有 SpringApplicationRunListeners 的 starting() 方法 listeners.starting(); Collection exceptionReporters; try { // 封裝命令行參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 準備環(huán)境,包括創(chuàng)建環(huán)境,創(chuàng)建環(huán)境完成后回調(diào) SpringApplicationRunListeners#environmentPrepared()方法,表示環(huán)境準備完成 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); // 打印 Banner Banner printedBanner = this.printBanner(environment); // 創(chuàng)建 IOC 容器(決定創(chuàng)建 web 的 IOC 容器還是普通的 IOC 容器) context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); /* * 準備上下文環(huán)境,將 environment 保存到 IOC 容器中,并且調(diào)用 applyInitializers() 方法 * applyInitializers() 方法回調(diào)之前保存的所有的 ApplicationContextInitializer 的 initialize() 方法 * 然后回調(diào)所有的 SpringApplicationRunListener#contextPrepared() 方法 * 最后回調(diào)所有的 SpringApplicationRunListener#contextLoaded() 方法 */this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新容器,IOC 容器初始化(如果是 Web 應(yīng)用還會創(chuàng)建嵌入式的 Tomcat),掃描、創(chuàng)建、加載所有組件的地方 this.refreshContext(context); // 從 IOC 容器中獲取所有的 ApplicationRunner 和 CommandLineRunner 進行回調(diào) this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } // 調(diào)用 所有 SpringApplicationRunListeners#started()方法 listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }小結(jié):@SpringBootApplication 注解
@SpringBootConfiguration
@EnableAutoConfiguration 注解@AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration {@Import({Registrar.class}),導(dǎo)入了一個 Registrar 的組件。關(guān)于 @Import 的用法文章上面也有介紹哦。@Import({AutoConfigurationImportSelector.class})getAutoConfigurationEntry() -> getCandidateConfigurations() -> loadFactoryNames()。loadFactoryNames() 方法傳入了 EnableAutoConfiguration.class 這個參數(shù)。先記住這個參數(shù),等下會用到。
META-INF/spring.factories 這類文件是什么就不懵了。當然在很多第三方依賴中都會有這個文件,一般每導(dǎo)入一個第三方的依賴,除了本身的jar包以外,還會有一個 xxx-spring-boot-autoConfigure,這個就是第三方依賴自己編寫的自動配置類。我們現(xiàn)在就以 spring-boot-autocongigure 這個依賴來說。可以看到 EnableAutoConfiguration 下面有很多類,這些就是我們項目進行自動配置的類。 一句話:將類路徑下 META-INF/spring.factories 里面配置的所有 EnableAutoConfiguration 的值加入到 Spring 容器中。 HttpEncodingAutoConfiguration 通過上面方式,所有的自動配置類就被導(dǎo)進主配置類中了。但是這么多的配置類,明顯有很多自動配置我們平常是沒有使用到的,沒理由全部都生效吧。 接下來我們以 HttpEncodingAutoConfiguration為例來看一個自動配置類是怎么工作的。為啥選這個類呢?主要是這個類比較的簡單典型。 先看一下該類標有的注解:
@EnableConfigurationProperties({HttpProperties.class})把配置文件中的配置項與當前 HttpProperties 類綁定上了。@ConfigurationProperties( prefix = 'spring.http' )// 從配置文件中獲取指定的值和bean的屬性進行綁定 public class HttpProperties { }小結(jié):
不知道小伙伴們有沒有發(fā)現(xiàn),很多需要待加載的類都放在類路徑下的META-INF/Spring.factories 文件下,而不是直接寫死這代碼中,這樣做就可以很方便我們自己或者是第三方去擴展,我們也可以實現(xiàn)自己 starter,讓SpringBoot 去加載?,F(xiàn)在明白為什么 SpringBoot 可以實現(xiàn)零配置,開箱即用了吧! |
|
|
來自: 西北望msm66g9f > 《培訓(xùn)》