电子说
我们知道,在使用SpringBoot的时候,我们只需要如下方式即可直接启动一个Web程序:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
和我们之前使用普通Spring时繁琐的配置相比简直不要太方便,那么你知道SpringBoot实现这些的原理么
首先我们看到类上方包含了一个@SpringBootApplication
注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class?[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class?[] scanBasePackageClasses() default {};
}
这个注解上边包含的东西还是比较多的,咱们先看一下两个简单的热热身
@ComponentScan
注解@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
这个注解咱们都是比较熟悉的,无非就是自动扫描并加载符合条件的Bean到容器中,这个注解会默认扫描声明类所在的包开始扫描,例如:
类cn.shiyujun.Demo
类上标注了@ComponentScan
注解,则cn.shiyujun.controller
、cn.shiyujun.service
等等包下的类都可以被扫描到
这个注解一共包含以下几个属性:
basePackages:指定多个包名进行扫描
basePackageClasses:对指定的类和接口所属的包进行扫
excludeFilters:指定不扫描的过滤器
includeFilters:指定扫描的过滤器
lazyInit:是否对注册扫描的bean设置为懒加载
nameGenerator:为扫描到的bean自动命名
resourcePattern:控制可用于扫描的类文件
scopedProxy:指定代理是否应该被扫描
scopeResolver:指定扫描bean的范围
useDefaultFilters:是否开启对@Component,@Repository,@Service,@Controller的类进行检测
@SpringBootConfiguration
注解这个注解更简单了,它只是对Configuration
注解的一个封装而已
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
EnableAutoConfiguration
注解这个注解可是重头戏了,SpringBoot号称的约定大于配置,也就是本文的重点自动装配的原理就在这里了
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class?[] exclude() default {};
String[] excludeName() default {};
}
简单概括一下,这个注解存在的意义就是:利用@Import
注解,将所有符合自动装配条件的bean注入到IOC容器中,关于@Import
注解原理这里就不再阐述。
进入类AutoConfigurationImportSelector
,观察其selectImports
方法,这个方法执行完毕后,Spring会把这个方法返回的类的全限定名数组里的所有的类都注入到IOC容器中
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List
观察上方代码:
protected boolean isEnabled(AnnotationMetadata metadata) {
return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
}
private static final String[] NO_IMPORTS = new String[0];
org.springframework.boot.autoconfigure
包下的META-INF/spring-autoconfigure-metadata.properties
文件中@ConditionalXXX
注解。org.springframework.boot.actuate.autoconfigure.web.servlet.WebMvcEndpointChildContextConfiguration.ConditionalOnClass=org.springframework.web.servlet.DispatcherServlet
org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,io.micrometer.core.instrument.MeterRegistry
org.springframework.boot.actuate.autoconfigure.flyway.FlywayEndpointAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
具体的加载代码就不列出了,无法就是个读取配置文件
这里放个加载之后的结果图:
获取@EnableAutoConfiguration
注解上的exclude、excludeName属性,这两个属性的作用都是排除一些类的
这里又是关键的一步,可以看到刚才图片中spring-autoconfigure-metadata.properties文件的上方存在一个文件spring.factories,这个文件可就不止存在于org.springframework.boot.autoconfigure
包里了,所有的包里都有可能存在这个文件,所以这一步是加载整个项目所有的spring.factories文件。这个文件的格式是这样的
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoConfiguration,\\org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\\org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration
这里存在一个知识点,SpringBoot中的star就是依靠这个文件完成的,假如我们需要自定义一个SpringBoot的Star,就可以在我们的项目的META-INF文件夹下新建一个spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.shiyujun.TestAutoConfiguration
这样当别的项目依赖我们的项目时就会自动把我们的TestAutoConfiguration
类注入到Spring容器中
@ConditionalXXX
系列注解是否满足需求到了这里我们已经把SpringBoot自动装配的原理搞清楚了,但是总感觉差点什么,那我们从这些自动装配的类里面挑一个我们比较熟悉的关于Servlet的类来看看咋回事吧:
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
public class ServletEndpointManagementContextConfiguration {
public ServletEndpointManagementContextConfiguration() {
}
@Bean
public ExposeExcludePropertyEndpointFilter
自上而下观察整个类的代码,你会发现这些自动装配的套路都是一样的
ResourceConfig
以及不存在类DispatcherServlet
时装配JerseyServletEndpointManagementContextConfiguration
DispatcherServlet
类时装配WebMvcServletEndpointManagementContextConfiguration
全部0条评论
快来发表一下你的评论吧 !