Sping Boot 学习-进阶
Posted 菜鸟-肥龙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sping Boot 学习-进阶相关的知识,希望对你有一定的参考价值。
一、Spring Boot 自动装配原理
1.1、核心注解解析
1、@SpringBootApplication
- @SpringBootApplication 是 SpringBoot 启动类上的注解,标志着该类是一个启动类。
- @SpringBootApplication 是一个复合注解,其中主要包括 @SpringBootConfiguration、SpringBootConfiguration、SpringBootConfiguration
- 注解关系如下图:
2、@SpringBootConfiguration
- 是一个自定义注解,点进去之后可以看见其是由 元注解 + @Configuration 组成。
- @Configuration 表明该类是一个配置类。
- @Configuration 注解点进去可以看见 @Component 注解,说明其被 Spring IOC 容器所管理。
3、@EnableAutoConfiguration
- 见名知意,@EnableAutoConfiguration 是自动装配的核心注解。
- @EnableAutoConfiguration 是一个复合注解,其组成注解包括 @AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class)
4、@AutoConfigurationPackage
- @AutoConfigurationPackage 也是一个复合注解,其由 元注解 + @Import(AutoConfigurationPackages.Registrar.class) 注解组成。
- 这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是AutoConfigurationPackages.Registrar.class 类。
- 就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意。
5、@ComponentScan
- 扫描被
@Component
(@Service
,@Controller
)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilter
和AutoConfigurationExcludeFilter
。
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) )
1.2、核心类解析
1、AutoConfigurationPackages.Registrar.class
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
register(registry, new PackageImport(metadata).getPackageName());
- metadata :主配置类的全路径类名。
- debug 可以看见 ‘’new PackageImport(metadata).getPackageName()‘’,获得是主配置所在包下的包名。
2、AutoConfigurationImportSelector.class
AutoConfigurationImportSelector
类实现了ImportSelector
接口,也就实现了这个接口中的selectImports
方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
@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());
===================================================================================
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata)
if (!isEnabled(annotationMetadata))
return EMPTY_ENTRY;
AnnotationAttributes attributes = getAttributes(annotationMetadata);
\'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);
return new AutoConfigurationEntry(configurations, exclusions);
=====================================================================================
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null)
return result;
try
Enumeration<URL> urls = (classLoader != null ?
\'classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
\'ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));\'
result = new LinkedMultiValueMap<>();
- Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。
`AutoConfigure/META-INF/spring.factories: 里面包含了所有springBoot整合的类的全路径类名,基于反射射原理通过类加载器就能获取类的实例对象 . ”\\ “是换行使用的
`AutoConfigure/META-INF/spring-configation-metadata.json: 里面配置所有springBoot整合的类的相关默认配置.
1.3、@Condition
# EnableAutoConfiguration 能获得所有的SpringBoot整合的类的实例化对象,但并不是每个类它都会将创建并注入SpringIOC容器之中,只有引入了相关的依赖,其才会将类创建对象.@Condition是实现这一个功能的注解.
Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载相应的Bean
@Conditional要配合Condition的实现类(ClassCondition)进行使用.
`在AutoConfigure/org.springframework.boot.autconfigure/comdition 文件下springboot提供了大量的条件注解,其中常用的有:
ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
1.4、@Import
# @Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
①导入Bean
②导入配置类
③导入 ImportSelector 实现类。一般用于加载配置文件中的类
④导入 ImportBeanDefinitionRegistrar 实现类。
- 导入Bean @Import(User.class)
- 导入配置类 @Import(UserConfig.class)
二、Spring Boot 零配置原理
2.1、Spring Boot 零配置原理分析
SpringBoot框架主要的特性就是简化Spring开发的过程,它只是整合了Sprin并没有对其做什么其他的改动。Spring在开发过程中就提供配置文件(Application.xml)或纯注解开发两种模式,在纯注解开发的过程中就已经不需要配置任何的配置文件了。在配置文件模式下开发,就需要提供 \'SpringApplication.xml\' \'SpringMVCApplication.xml\' \'Web.xml\'等三个比较主要的配置文件。
\'SpringApplication.xml\' & \'SpringMVCApplication.xml\'配置文件可以通过创建相应的配置类来替代掉,在对应的配置中添加 \'@Configuration\' + \'@Bean\'注解。
Spring的IOC容器和SpringMVC中的IOC容器具有父子容器的概念,SpringMVC的IOC容器为子容器,Spring的IOC容器为父容器,子容器中的Bean能调用父容器中的方法,父容器中的方法不能调用子容器中的Bean.
当启动一个\'WEB\'项目时,容器(Tomcat)首先会读取项目\'web.xml\'配置文件里的配置。所以要替换掉\'web.xml\'文件就得在项目启动之初就能被加载到。这里就涉及到一个\'SPI\'机制的概念。
\'SPI\'是Servlet3.0之后推出的一种规范,是JDK内置的一种服务发现机制,Java提供的一套被第三方实现或扩展的API。它规定在jar包的\'META-INFO/services\'目录下创建以\'服务接口全路径类名命名的文件\',该文件里是该接口的具体\'实现类的全路径类名\'。当外部的程序(Tomcat)装配这个模块时,就能通过\'(SpringMVC)jar/META-INFO/services/\'里的配置文件找到具体的实现类的全路径类名,然后通过反射机制获取对应类的实例化对象。
找到对应的SpringMVV的jar包,找到META-INFO/services/。该目录下就有一个SevletContainerInitializer接口的全路径类名为文件名的文件,文件内容就是该接口的具体实现类-SpringServletContainerInitalizer。
\'SpringServletContainerInitalizer\':在这个类的上方定义了一个\'@HandlesTypes\'注解,该注解中配置类\'WebApplicationInitializer.class\'。它会将\'WebApplicationInitializer\'所有的实现类添加到\'onStartup()\'方法的\'Set<Class<?>>\'集合之中(\'ServletContainerInitializer\'接口中只有\'onStartup()\'一个方法).
SpringServletContainerInitializer类中会将所有\'WebApplicationInitializer\'接口的实现类实例化,然后循环执行\'onStartup(servletContext)\'方法.
2.2、SPI 机制
# 1.什么是SPI机制
SPI(service provider Interface) ,是JDK内置的一种服务发现机制,是java提供的一套被第三方实现或扩展的API.它规定在\'org.springframework:spring-web.jar\'包的\'META-INFO/services\'目录下创建一个以\'服务接口全路径类名命名的文件\',该文件里是该接口的具体\'实现类的全路径类名\'.当外部程序(Tomcat)装配这个模块是,就能通过\'jar/META-INFO/services/\'里的配置文件找到具体的实现类的全路径类名,通过反射机制完成实例化.
\'org.springframework.web.SpringServletContainerInitializer\'
# 2.为什么一定要在 classes 中的 META-INFO/services 下呢?
JDK提供服务实现查找的一个工具类: java.util.ServiceLoader 在这个类里面写死了
//默认会去这里寻找相关信息
private static final String PREFIX = \'META-INFO/services\';
2.3、SpringServletContainerInitializer 分析
1.SpringServletContainerInitializer类的作用
\'@HandlesTypes\'注解中配置\'WebApplicationInitializer.class\',它会将\'WebApplicationInitializer\'所有的实现类添加到\'onStartup()\'方法的\'Set<Class<?>>\'集合之中(\'ServletContainerInitializer\'接口中只有\'onStartup()\'一个方法).
SpringServletContainerInitializer类中会将所有\'WebApplicationInitializer\'接口的实现类实例化,然后循环执行\'onStartup(servletContext)\'方法.
三、内置Tomcat原理解析
SpringBoot 在启动的时候会加载到\'TomcatWebServer\' 类对象,执行里面的初始化方法,初始方法中会调用一个\'tomcat.start()\',将内置tomcat进行启动.
Apache提供了一款内嵌式的Tomcat,引入相关依赖让项目可以以 jar 的形式发布, 其中有一个\'Tomcat\'的类,可以创建器对象,\'tomcat.start()\',能启动整个tomcat项目.
以上是关于Sping Boot 学习-进阶的主要内容,如果未能解决你的问题,请参考以下文章
D15 Sping Boot 入门 Sping框架--Java Web之书城项目 购物车模块
D08 Sping Boot 入门 Sping框架--Java Web之JSP
D16 Sping Boot 入门 Sping框架--Java Web之书城项目 dingda模块
D16 Sping Boot 入门 Sping框架--Java Web之书城项目 dingda模块