main方法剖析
我们都知道Springboot的main方法启动非常的方便,那么它是如何启动的呢,一起来剖析一下。
下面这是一个Springboot 启动的常规方法,映入眼帘的就是 @SpringBootApplication
,与SpringApplication
静态类调用的run()
方法。我们不清楚的前提下,一层层的点进去看。
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
首先点击 @SpringBootApplication
,跳转到次注解内部。代码如下,其中@Target
、@Retention````@Documented
、@Inherited
,这四个注解我们都很熟悉,是对注解定义的元注解。我们点进去@SpringBootConfiguration
看一下。
@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 {
省略...
}
可以看到下图,@SpringBootConfiguration
这个注解就是@Configuration
一层封装,用与在Springboot中再次封装一次。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
回到上层,点击进入@EnableAutoConfiguration
,看看这个注解有什么内容。首先看到的是,这个注解定义中,注入了一个AutoConfigurationImportSelector.class
配置类,我们都知道@Import
注解的作用是将一个配置类加载到IOC容器里。那我们看看AutoConfigurationImportSelector.class
类的内容是什么。可以看到这个注解跟我们平常理解的带有@configuration
标注的配置类不同,它是以个普通类,那么一个普通类是怎么要引入到IOC容器当中呢。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
我们来研究一下 @Import
注解在Spring中的用法。
@import
注解使用方法
1. 直接引入外部带@configuration
的配置类。
这个用法比较常见,写一个简单的配置类Demo
@Configuration
public class PersonConfig{
@Bean
public String getPerson(){
System.out.println("调用方法generateName");
return new person();
}
}
测试方法如下,
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersonConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean.getPerson());
}
}
2. 直接注入普通配置类
public class Person {
public String generateName(){
System.out.println("调用方法generateName");
return "gq";
}
}
@Configuration
@Import(Person.class)
public class ConfigA {
public Person getPerson(){
return new Person();
}
}
3.继承 importSelector 接口
selectImports()方法返回的为需要加载入IOC的容器的配置类,例如redis、mysql等需要配置文件的组件类的全路径。返回的全路径会被加载入IOC容器当中。需要三个类
PersonSelector .class
public class PersonSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//在这里会返回所有,需要自动装配的配置类
return new String[]{
"com.dubbog.provider.config.person"
};
}
}
EnableASelector.class 定义一个注解。引入这个注解就会引入PersonSelector.class
配置类
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Configuration
@Import(PersonSelector.class)
public @interface EnablePersonSelector {
}
Test .class 用于 测试
@EnablePersonSelector
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Test.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
输出结果,为根绝全路径在IOC容器中获取的Person实例。
com.dubbog.provider.config.Person@4b44655e
AutoConfigurationImportSelector.class的作用
我们已经知道了@import
的使用方法已经作用。现在我们看看Springboot使用的@import
干了点什么。如下是AutoConfigurationImportSelector.class中的 selectImports() 方法。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
我们已经知道,selectImports方法,会在启动时,被Spring内部方法调用,获取到需要加载到IOC内部的全路径类名称。所以此时,selectImports方法实现的逻辑就是从一个地方读取到所有需要加载到IOC内部的配置类数组。这里是字符串数组。
其实至此。我们已经知道了Springboot是如何实现自动化配置的。就是Springboot已经提前将一堆配置类写好,然后读取他们加载到IOC容器当中。
还有许多东西其实还没有剖析清楚。例如,如何调用importSelector方法。SpringApplication的run方法是如何启动的。我们抽时间在看吧。