致力于最高效的Java学习
1. SpringBoot 源码常用注解拾遗组合注解
@Value@Component public class Person {
@Value('i am name') private String name;
}
@Value(${key}) 的方式获取全局配置文件中的指定配置项。@ConfigurationProperties@Value 的方式去配置项需要一个一个去取,这就显得有点 low 了。我们可以使用 @ConfigurationProperties 。@ConfigurationProperties 的类的所有属性和配置文件中相关的配置项进行绑定。(默认从全局配置文件中获取配置值),绑定之后我们就可以通过这个类去访问全局配置文件中的属性值了。person.name=kundy person.age=13 person.sex=male
@ConfigurationProperties 有一个 prefix 参数,主要是用来指定该配置项在配置文件中的前缀。@Import
1. 直接导入普通的 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) 这个方法。
创建 SpringApplication 对象
运行 run() 方法public ConfigurableApplicationContext run(String... args) {
// 创建计时器 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); // 回调所有 SpringApplicationRunListeners 的 starting() 方法 listeners.starting(); Collection exceptionReporters; try { // 封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 准备环境,包括创建环境,创建环境完成后回调 SpringApplicationRunListeners#environmentPrepared()方法,表示环境准备完成 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); // 打印 Banner Banner printedBanner = this.printBanner(environment); // 创建 IOC 容器(决定创建 web 的 IOC 容器还是普通的 IOC 容器) context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); /* * 准备上下文环境,将 environment 保存到 IOC 容器中,并且调用 applyInitializers() 方法 * applyInitializers() 方法回调之前保存的所有的 ApplicationContextInitializer 的 initialize() 方法 * 然后回调所有的 SpringApplicationRunListener#contextPrepared() 方法 * 最后回调所有的 SpringApplicationRunListener#contextLoaded() 方法 */ this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新容器,IOC 容器初始化(如果是 Web 应用还会创建嵌入式的 Tomcat),扫描、创建、加载所有组件的地方 this.refreshContext(context); // 从 IOC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调 this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } // 调用 所有 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); } } 小结:@SpringBootApplication 注解
@SpringBootConfiguration
@EnableAutoConfiguration 注解@AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { @Import({Registrar.class}) ,导入了一个 Registrar 的组件。关于 @Import 的用法文章上面也有介绍哦。@Import({AutoConfigurationImportSelector.class})getAutoConfigurationEntry() -> getCandidateConfigurations() -> loadFactoryNames() 。loadFactoryNames() 方法传入了 EnableAutoConfiguration.class 这个参数。先记住这个参数,等下会用到。
META-INF/spring.factories 这类文件是什么就不懵了。当然在很多第三方依赖中都会有这个文件,一般每导入一个第三方的依赖,除了本身的jar包以外,还会有一个 xxx-spring-boot-autoConfigure,这个就是第三方依赖自己编写的自动配置类。我们现在就以 spring-boot-autocongigure 这个依赖来说。可以看到 EnableAutoConfiguration 下面有很多类,这些就是我们项目进行自动配置的类。 一句话:将类路径下 META-INF/spring.factories 里面配置的所有 EnableAutoConfiguration 的值加入到 Spring 容器中。 HttpEncodingAutoConfiguration 通过上面方式,所有的自动配置类就被导进主配置类中了。但是这么多的配置类,明显有很多自动配置我们平常是没有使用到的,没理由全部都生效吧。 接下来我们以 HttpEncodingAutoConfiguration为例来看一个自动配置类是怎么工作的。为啥选这个类呢?主要是这个类比较的简单典型。 先看一下该类标有的注解:
@EnableConfigurationProperties({HttpProperties.class}) 把配置文件中的配置项与当前 HttpProperties 类绑定上了。@ConfigurationProperties( prefix = 'spring.http' )// 从配置文件中获取指定的值和bean的属性进行绑定 public class HttpProperties { } 小结:
不知道小伙伴们有没有发现,很多需要待加载的类都放在类路径下的META-INF/Spring.factories 文件下,而不是直接写死这代码中,这样做就可以很方便我们自己或者是第三方去扩展,我们也可以实现自己 starter,让SpringBoot 去加载。现在明白为什么 SpringBoot 可以实现零配置,开箱即用了吧! |
|
来自: 西北望msm66g9f > 《培训》