1、Springboot内嵌容器原理
2、Springboot入口注解
3、Springboot的类扫描
使用spring源码模拟Springboot的内嵌容器
下面的程序也是springMVC源码调试程序。
1、build.gradle
plugins {
id 'java'
}
group 'org.springframework'
version '5.1.10.BUILD-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile project(':spring-context')
compile project(':spring-webmvc')
testCompile group: 'junit', name: 'junit', version: '4.12'
provided group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.21'
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.47'
}
2、启动类和配置类
public class SpringbootStart {
// 启动类
public static void main(String[] args) {
WebApplication.run(SpringbootStart.class);
}
}
public class WebApplication {
/**
* 获取系统的临时目录
* System.getProperty("java.io.tmpdir")
*/
public static void run(Class clazz){
// 初始化spring的环境
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
// context.setServletContext(servletContext);
// 添加@EnableWebMvc注解时需要注释掉刷新这里,不然会出现
// Error creating bean with name 'resourceHandlerMapping' defined in DelegatingWebMvcConfiguration
// 没有向AnnotationConfigWebApplicationContext中设置 servletContext,解决方法就是在刷新前设置servletContext或者不刷新
context.refresh();
// 获取系统的临时目录
File file = new File(System.getProperty("java.io.tmpdir"));
// 创建tomcat,将web环境关联到tomcat上
Tomcat tomcat = new Tomcat();
tomcat.setPort(9090);
Context ctx = tomcat.addContext("/", file.getAbsolutePath());
// 创建注册servlet
DispatcherServlet servlet = new DispatcherServlet(context);
Tomcat.addServlet(ctx, "springmvc", servlet).setLoadOnStartup(0);
ctx.addServletMapping("/","springmvc");
try {
// 启动tomcat
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
@Configuration
@ComponentScan("top.gmfcj")
public class AppConfig {}
@Controller
public class IndexController {
@RequestMapping("/index")
@ResponseBody
public String index(){
System.out.println("index controller");
return "index";
}
}
看一下springboot中对tomcat容器的处理
// TomcatServletWebServerFactory#getWebServer
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
// baseDir
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 连接器
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
// 配置host的engine
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
// 启动tomcat
return getTomcatWebServer(tomcat);
}
Springboot入口
SpringBoot的入口类被@SpringBootApplication注解标注,说明这个类就是SpringBoot的主配置类,可以运行这个主配置类的main方法来启动SpringBoot项目。
@Target({ElementType.TYPE}) // 指定注解可以标注的范围
@Retention(RetentionPolicy.RUNTIME) // 注解存活的范围
@Documented // 注解可以被javadoc识别
@Inherited // 注解到了一个类上是,整个注解会一直继承下去,其他则只起标识作用
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}
@SpringBootConfiguration
@SpringBootConfiguration:表示一个类是SpringBoot的配置类,底层是Spring的注解@Configuration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}
@Configuration标注的类不可以是final类型(无法进行cglib代理)扫描出来会验证@Bean和@Configuration
@EnableAutoConfiguration
@EnableAutoConfiguration:开启自动配置功能
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
EnableAutoConfiguration注解上有包含了一个@AutoConfigurationPackage注解和导入了AutoConfigurationImportSelector类
@AutoConfigurationPackage
@AutoConfigurationPackage:自动配置包
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}
Registrar静态类:注册AutoConfigurationPackages类
// 实现了ImportBeanDefinitionRegistrar接口 注册AutoConfigurationPackages.PackageImport类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
// 存储基本包名称
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// getPackageName方法就是去获取@AutoConfigurationPackage注解所在的包名,在这里就是@SpringBootApplication注解类的包名
// 因为这里的metadata -> @AutoConfigurationPackage
register(registry, new PackageImport(metadata).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
// bdMap中包含了 org.springframework.boot.autoconfigure.AutoConfigurationPackages
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
// 如果已经存在bdMap中了,那么就通过基本包,构建这个 AutoConfigurationPackages bean对象
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 不存在bdMap中,那么注册一个beanName = org.springframework.boot.autoconfigure.AutoConfigurationPackages
// 的 beanDefinition
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
@EnableConfigurationProperties
@EnableConfigurationProperties实现properties文件自动装配原理
导入了EnableConfigurationPropertiesImportSelector类
注入了两个类,都实现了ImportBeanDefinitionRegistrar接口
ConfigurationPropertiesBeanRegistrar读取ConfigurationProperties注解并注册被注解的类
-
ConfigurationPropertiesBindingPostProcessorRegistrar注册两个类
- ConfigurationPropertiesBindingPostProcessor(BeanPostProcessor)绑定ConfigurationProperties类的属性和配置文件中的值
- ConfigurationBeanFactoryMetadata(BeanFactoryPostProcessor)缓存所有bean对象的工厂方法
-
读取注解中的前缀,使用该注解的类的类型。就会对比前缀.属性与properties配置中的key,相同就会注入,最终将组装好的对象交由spring
@Import(EnableConfigurationPropertiesImportSelector.class) public @interface EnableConfigurationProperties {} // 向容器中注入两个类 // ConfigurationPropertiesBeanRegistrar // ConfigurationPropertiesBindingPostProcessorRegistrar class EnableConfigurationPropertiesImportSelector implements ImportSelector { private static final String[] IMPORTS = { ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() }; @Override public String[] selectImports(AnnotationMetadata metadata) { return IMPORTS; } }
AutoConfigurationImportSelector
@EnableAutoConfiguration注解中导入了AutoConfigurationImportSelector类,AutoConfigurationImportSelector使用了SpringFactoriesLoader.loadFactoryNames方法直接加载类所在路径的META-INF/spring.factories文件
// annotationMetadata 这里就代表 EnableAutoConfiguration 注解上的所有注解信息
public class AutoConfigurationImportSelector implements DeferredImportSelector{
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
// 不开启自动装配
return NO_IMPORTS;
}
// load META-INF/spring-autoconfigure-metadata.properties 文件中的信息
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
// 从 META-INF/spring.factories 文件中获取key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
// 根据 @EnableAutoConfiguration 注解属性,进行筛选
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
// 返回所有的自动装配类
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解中的所有属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从META-INF/spring.factories中获取配置信息 key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 筛选,去除排除的自动装配
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
// 发布事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回包含自动装配集合和不自动装配集合的entry
return new AutoConfigurationEntry(configurations, exclusions);
}
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
// 获取配置文件中 key = spring.boot.enableautoconfiguration, 的值
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// META-INF/spring.factories文件中key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
// getSpringFactoriesLoaderFactoryClass() => EnableAutoConfiguration.class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
return configurations;
}
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 拿到META-INF/spring.factories文件中 key=org.springframework.boot.autoconfigure.AutoConfigurationImportListener 的监听器对象集合
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
// 装配不同的环境
invokeAwareMethods(listener);
// 发布自动装配事件
listener.onAutoConfigurationImportEvent(event);
}
}
}
这个文件中就申明了有哪些自动配置
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
Springboot的包扫描
在springboot中会默认扫描启动类所在包下的所有子包中的注解类,这种情况是由@ComponentScan注解引起的。无论springboot中创建的是哪一种容器,在spring中对ConfigurationClassPostProcessor的回调是不会发生改变的。因此springboot中包扫描和spring的包扫描一致,都是在ConfigurationClassPostProcessor类中进行包的扫描和bd的注册。
直接跳到spring的包扫描的逻辑分析
// ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
if (basePackages.isEmpty()) {
// 如果没有配置basePacke,则会将当前class的包名作为 basePackage
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
// 正式开始扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
而当前注解了@ComponentScan的类就是启动类,所以会扫描启动类所在包下的所有子包。