赞
踩
本文首发于 vivo互联网技术 微信公众号
链接:https://mp.weixin.qq.com/s/NvPO5-FWLiOlrsOf4wLaJA
作者:Li Wanghong
SpringBoot作为Java领域非常流行的开源框架,集成了大量常用的第三方库配置,Spring Boot应用中这些第三方库几乎可以是零配置的开箱即用,大部分的 Spring Boot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。SpringBoot上手快,但是如果你的项目中业务场景需要一些特殊定制,甚至对源码进行定制化,那这时候了解原理就变成必需的了,只有充分了解源码,知道框架底层的工作原理,才能对源码中原有的机制进行修改 / 扩展等等。本文介绍了SpringBoot如何解析配置类、如何集成第三方配置。
在SpringBoot中推荐基于Java Config的方式来代替传统的XML方式去引入Bean,本文就是分析SpringBoot如何解析这些配置类,为容器中注入我们自定义的以及SpringBoot为我们提供的Bean。SpringBoot版本基于2.1.7.RELEASE。
- // 通常一个SpringBoot工程会含有这样一个主配置类,它位于我们项目的根包下,通过启动这个main方法就可以启动我们的项目
- // 下面我们先分析@SpringBootApplication注解有哪些作用,在第二节中分析run方法,在run方法中会进行配置类的解析
- @SpringBootApplication
- public class SpringbootApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringbootApplication.class, args);
- }
- }
- // 点击@SpringBootApplication进去发现它其实是由三个核心注解构成的,下面分别讲解这三个注解
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
- @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
- public @interface SpringBootApplication {
- // 点进去发现它其实就是一个@Configuration注解,SpringBoot解析到就会知道这是一个配置类,会给容器中引入一些bean
- // 一个被@Configuration标注的类,相当于一个applicationContext.xml文件
- // @Configuration点进去发现其实就是一个@Component注解
- @Configuration
- public @interface SpringBootConfiguration {
-
- }
2、@EnableAutoConfiguration注解
- // 结合下面@AutoConfigurationPackage注解,发现@EnableAutoConfiguration注解就是通过@Import注解给容器中引入了两个bean,
- // 分别是AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar,通过这两个类可以给容器中引入更多的类
- // 下面先介绍下@Import注解的使用
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
- public @interface EnableAutoConfiguration {
- }
-
- @Import(AutoConfigurationPackages.Registrar.class)
- public @interface AutoConfigurationPackage {
- }
@Import注解
在原生SpringFramework中,装配组件有下面三种方式
如果要注册较多的Bean,通过1) 2) 两种方式不太方便,可以通过Spring提供的模块装配功能,通过给配置类标注@Enable注解,再在注解上标注@Import注解,即可完成组件装配的效果,下面通过一个例子讲解@EnableXXX和@Import的使用。
- // 步骤1)
- // 创建几个动物的实体类,如Cat、Bird、Chicken、Dog、Duck、Pig、Sheep、Snake、Tiger
- // 接下来会通过@Import的各种用法将这些bean注入到容器中
- @Data
- public class Cat {
- private String name;
- }
- // 步骤2) 在主配置类上加上我们自定义的@EnableAnimal注解,含义就是通过@EnableAnimal会给容器中导入上述的那些动物Bean
- // @EnableAnimal就可以类比我们上面说的@EnableAutoConfiguration注解,两者功能类似
- @SpringBootApplication
- @EnableAnimal
- public class SpringbootApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringbootApplication.class, args);
- }
- }
- // 步骤3)
- // @EnableAnimal如下所示,这是我们自定义的注解,这个注解的核心是是在上面声明了@Import注解
- // @Import注解可以传入四种类型,普通类、配置类、ImportSelector的实现类、ImportBeanDefinitionRegistar的实现类
- // 下面分别讲解@Import注解导入四种类型的用法
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.TYPE)
- @Import({Dog.class, AnimalRegisterConfiguration.class, AnimalImportSelector.class,
- AnimalImportBeanDefinitionRegistar.class})
- public @interface EnableAnimal {
- }
// 1) 直接@Import({Dog.class}),则容器启动完成之后容器中就有Dog这个bean,注意Dog这个类我们并没有通过@Component方式注入
- // 2) @Import ({AnimalRegisterConfiguration.class}),通过这种Import配置类形式也可以导入
-
- @Configuration
- public class AnimalRegisterConfiguration {
-
- @Bean
- public Cat cat(){
- return new Cat("cat");
- }
- }
- // 3) @Import({AnimalImportSelector.class}),通过返回一个Spring数组的方式,数组中可以方便的指定多个Bean
- // 我们上面说的AutoConfigurationImportSelector就是这种方式,SpringBoot给容器中导入MybatisAutoConfiguration
- // 等自动配置类就是通过这种方式导入的,第二节配置类解析会讲这个
- public class AnimalImportSelector implements ImportSelector {
-
- @Override
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- return new String[] {Tiger.class.getName()};
- }
- }
- // 4) @Import({AnimalImportBeanDefinitionRegistar.class}),通过手动编码方式向registry注入Bean
- // 我们上面说的AutoConfigurationPackages.Registrar就是这种方式
-
- public class AnimalImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar {
-
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- registry.registerBeanDefinition("snake", new RootBeanDefinition(Snake.class));
- }
- }
这个注解就是相当于XML中的<context:component-scan>,它会从定义的扫描包路径(默认是SpringBoot主配置所在的包及其子包)扫描标识了@Controller、@Service、@Repository、@Component注解的类到Spring容器中。
- // 我们可以看到这个@ComponentScan注解上显示指定了两个Filter过滤条件,它是SpringBoot提供的一种扩展机制,能让我们
- // 向IOC容器中注册一些自定义的组件过滤器,以便在包扫描的过程中过滤一些Bean
- @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
- @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
- // 这是TypeExcludeFilter的方法,可以看到它是获取容器中所有的TypeExcludeFilter,并遍历它们去过滤一些不想要的Bean
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {
- Collection<typeexcludefilter> delegates
- = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
- for (TypeExcludeFilter delegate : delegates) {
- if (delegate.match(metadataReader, metadataReaderFactory)) {
- return true;
- }
- }
- }
- return false;
- }
- // 我们可以自定义一个TypeExcludeFilter,它的功能就是过滤掉User这个bean,不让它注入到容器中
- @Component
- public class MyTypeExcludeFilter extends TypeExcludeFilter {
-
- @Override
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- return User.class.getName().equals(metadataReader.getClassMetadata().getClassName());
- }
- }
-
- @Component
- public class User {
- private String name;
- }
- // 可以看到AutoConfigurationExcludeFilter就是将自动配置类给过滤掉了,因为自动配置类xxxAutoConfiguration是通过
- // 上面说的AutoConfigurationImportSelector方式导入的,没必要导入两次
- public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
- }
- }
我们说SpringBoot约定大于配置,它通过一些xxxAutoConfiguration给容器中导入了一些组件,如果你需要但是没有配置视图解析器,则SpringBoot就会提供其默认配置的视图解析器,这样就简化了配置。那么如果自己定义了一个视图解析器,那么到底SpringBoot会往容器中注入哪个呢? 查看下面默认注入的视图解析器代码,发现其上面有一个@ConditionalOnMissingBean注解,意思就是若容器中没有这个则容器会给你注入一个这样的视图解析器,若容器中有就不注入了。
- // @ConditionalOnMissingBean是通过@Conditional注解和Condition接口的实现类(OnBeanCondition.class)来实现这个效果的
- @Bean
- @ConditionalOnMissingBean
- public InternalResourceViewResolver defaultViewResolver() {
- @Conditional(OnBeanCondition.class)
- public @interface ConditionalOnMissingBean {
- @FunctionalInterface
- public interface Condition {
- boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
- }
我们也可以自己实现一个自定义的条件注解。
- // 自定义一个Condition接口的实现类MyCondition,通过其matches方法来判断是否符合指定条件
- public class MyCondition implements Condition {
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- // 获取注解MyConditionAnotation注解上指定value对应的值,如果没有这个值,则不符合条件
- String[] value = (String[]) metadata.getAnnotationAttributes(MyConditionAnotation.class.getName()).get("value");
- for (String property : value){
- if(StringUtils.isEmpty(context.getEnvironment().getProperty(property))){
- return false;
- }
- }
- return true;
- }
- }
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ ElementType.TYPE, ElementType.METHOD })
- @Documented
- @Conditional(MyCondition.class)
- public @interface MyConditionAnotation {
- String[] value() default {};
- }
- // 通过@Component给容器中注入一个A类型Bean,条件是@MyConditionAnotation注解指定的key1、key2对应的值有配置
- @Component
- @MyConditionAnotation({"key1", "key2"})
- public class A {
- }
类似Java的SPI、Dubbo的SPI机制,SpringBoot也提供了一种机制,它通过读取META-INF/spring.factories文件(这些文件可能存在于类路径中的多个jar包中)来加载一些预先配置的类,而这个核心机制来源于SpringFactoriesLoader。spring.factories文件必须采用 properties 格式,其中key是接口或抽象类的全限定名,而value是用逗号分隔的实现类的全限定类名列表。
- // 我们先来看下上面讲解@Import注解讲到的AutoConfigurationImportSelector,我们说通过它就可以导入SpringBoot提供那些
- // 自动配置类. 下面是Import注解讲到的AutoConfigurationImportSelector的selectImport方法
- @Override
- public String[] selectImports(AnnotationMetadata annotationMetadata) {
- // 忽略一些其他代码
- // 这里的含义就是通过SpringFactoriesLoader去加载META-INF/spring.factories中配置的那些xxxAutoConfiguration
- // 并放入String数组返回
- AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
- annotationMetadata);
- return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
- }
-
- // 忽略中间过程调用,后面会分析,接下来就走到这里通过SpringFactoriesLoader去加载自动配置类
- protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
- // 具体调用看下面分析
- List<string> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
- getBeanClassLoader());
- return configurations;
- }
-
- protected Class<?> getSpringFactoriesLoaderFactoryClass() {
- return EnableAutoConfiguration.class;
- }
- public static List<string> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
- // org.springframework.boot.autoconfigure.EnableAutoConfiguration
- String factoryClassName = factoryClass.getName();
- // 获得一个Map,map形如<key,List<String>>形式,后面getOrDefault是获取
- // org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的实现类集合
- // 注意这里面不仅仅是加载自动配置类,也会加载监听器、初始化器那些容器配置的实现类,这里统一一次加载之后放到缓存中
- return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
- }
-
- // 带缓存功能,从各个jar包的META-INF/spring.factories文件中加载实现类,一个key可能包含多个实现
- private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
- MultiValueMap<String, String> result = cache.get(classLoader);
- if (result != null) {
- // 缓存中有直接返回
- return result;
- }
-
- try {
- // FACTORIES_RESOURCE_LOCATION值是META-INF/spring.factories
- Enumeration<url> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
- ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
- result = new LinkedMultiValueMap<>();
- while (urls.hasMoreElements()) {
- URL url = urls.nextElement();
- UrlResource resource = new UrlResource(url);
- Properties properties = PropertiesLoaderUtils.loadProperties(resource);
- for (Map.Entry<?, ?> entry : properties.entrySet()) {
- // 获取key
- String factoryClassName = ((String) entry.getKey()).trim();
- // 将value逗号分隔,获得各个具体的实现类
- for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
- // 放入result中
- result.add(factoryClassName, factoryName.trim());
- }
- }
- }
- cache.put(classLoader, result);
- // 返回的result见下图
- return result;
- }
- }
可以看到返回了很多自动配置类,有AopAutoConfiguration、RabbitAutoConfiguration,也有MybatisAutoConfiguraion等(图中没有截全)
下面是自定义的一个BeanFactoryPostProcessor和BeanPostProcessor,发现通过BeanFactoryPostProcessor可以往容器中增加新的Bean或者修改原有的Bean定义,通过BeanPostProcessor可以修改已经创建好的Bean的属性值。
- @Component
- public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
-
- @Override
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
-
- // 往容器中新增BeanDefinition
- GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
- beanDefinition.setBeanClass(Chicken.class);
- registry.registerBeanDefinition("beanFactoryPostProcessor-Chicken", beanDefinition);
-
- // 修改容器中原有的BeanDefinition
- BeanDefinition snake = registry.getBeanDefinition("snake");
- snake.setLazyInit(true);
- }
- }
- @Component
- public class CatBeanPostProcessor implements BeanPostProcessor {
-
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- return bean;
- }
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- if (bean instanceof Cat){
- Cat cat = (Cat) bean;
- cat.setName("changeNameCat");
- }
- return bean;
- }
- }
第一节是SpringBoot解析自动配置类会用到的一些知识点,下面我们来看SpringBoot解析配置类的具体过程。上图是SpringBoot启动流程图,其中在refreshContext的第五步会调用容器的BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法。其中有一个是ConfigurationClassPostProcessor,它是在创建ConfigurableApplicationContext时设置到容器中的,如下所示。
- // 图中说的创建ConfigurableApplicationContext,默认创建的是普通的Servlet Web容器,就是下面这个
- // 通过反射创建会走到其默认的构造函数
- public AnnotationConfigServletWebServerApplicationContext() {
- // 这里面进去会走到下面代码
- this.reader = new AnnotatedBeanDefinitionReader(this);
- this.scanner = new ClassPathBeanDefinitionScanner(this);
- }
-
- // 走到这里
- registerAnnotationConfigProcessors(registry, null);
-
- // 走到这里
- // 向容器中注入一个ConfigurationClassPostProcessor,它是BeanFactoryPostProcessor
- if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
- RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
- def.setSource(source);
- beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
- }
-
- // 向容器中注入一个AutowiredAnnotationBeanPostProcessor,它是BeanPostProcessor,用于解决依赖注入的
- if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
- RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
- def.setSource(source);
- beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
- }
上面说到在refreshContex中的第五步时,会调用容器中的BeanFactoryPostProcessor
的postProcessBeanDefinitionRegistry方法。其中有一个是ConfigurationClassPostProcessor,这是我们解析自动配置类的入口,下面分析其postProcessBeanDefinitionRegistry方法。
- @Override
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
- // 删掉一些非关键代码
- processConfigBeanDefinitions(registry);
- }
下面的processConfigBeanDefinitions方法就是对应上图中的步骤1、2、3、4、5,其中步骤4和步骤5比较长,单独拆出来分析。
- public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
- List<beandefinitionholder> configCandidates = new ArrayList<>();
- // 获取容器中已注册的bean名字,见下图,注意,这里容器中这些BeanDefinition都是容器初始化过程中容器添加进去的
- // 不是我们业务代码的beanDefinition,这段代码其实是连贯的,为了注释图片方便才分开
- String[] candidateNames = registry.getBeanDefinitionNames();
-
- for (String beanName : candidateNames) {
- // 获取BeanDefinition
- BeanDefinition beanDef = registry.getBeanDefinition(beanName);
- // 判断这个BeanDefinition的configurationClass属性是不是full或者lite,如果是认为已经处理过了,第一次时默认为空,
- // 走下面分支
- if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
- ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
- // 打印日志记录下
- } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
- // 1) 下面先分析下这个checkConfigurationClassCandidate方法,这边看方法名也可以猜到是检测该类是不是配置类
- // 是配置类的意思就是它会给容器中引入bean,这个方法判断主要就是看这个类的元信息中有没有@Configuration注解
- // 有没有@Component注解、有没有@ComponentScan、@Import、@ImportResource注解,有没有@Bean方法
- // 构造一个BeanDefinitionHolder,放入configCandidates中
- configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
- }
- }
-
- // Return immediately if no @Configuration classes were found
- // 上图中容易默认已经引入了7个BeanDefinition,经过上面检测发现默认就一个符合条件的配置类,即我们的主配置类
- // 这里面configCandidates就一个,就是SpringBootApplication
- if (configCandidates.isEmpty()) {
- return;
- }
-
- // Sort by previously determined @Order value, if applicable
- // 排序
- configCandidates.sort((bd1, bd2) -> {
- int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
- int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
- return Integer.compare(i1, i2);
- });
-
- // 删掉部分代码
-
- // Parse each @Configuration class
- // 配置类解析工具
- ConfigurationClassParser parser = new ConfigurationClassParser(
- this.metadataReaderFactory, this.problemReporter, this.environment,
- this.resourceLoader, this.componentScanBeanNameGenerator, registry);
- // 待处理集合
- Set<beandefinitionholder> candidates = new LinkedHashSet<>(configCandidates);
- // 已处理集合
- Set<configurationclass> alreadyParsed = new HashSet<>(configCandidates.size());
- // 循环处理直到candidates.isEmpty()
- do {
- // 这边开始解析,对应步骤4
- parser.parse(candidates);
- parser.validate();
- // 取出第四步解析得到的一些configurationClasses集合
- Set<configurationclass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
- configClasses.removeAll(alreadyParsed);
-
- // 删除一部分代码
- // 这边也会去加载BeanDefinition,对应图中步骤五
- this.reader.loadBeanDefinitions(configClasses);
- alreadyParsed.addAll(configClasses);
- }
- while (!candidates.isEmpty());
- }
在配置类解析流程图中,第二步,会获取容器中已经注册的BeanDefinition,放入candidateNames中,然后依次遍历这些BeanDefinition,判断它有没有被处理过,如果处理过就不管,否则通过checkConfigurationClassCandidate方法去判断它是不是配置类,判断方法如下。通过阅读这段代码,发现如果一个类上面有@Configuration注解、或者有@Component、@ComponentScan、@Import、@ImportResource注解、或者有@Bean标注的方法,则认为它是一个配置类。默认情况下,走到这里时最终只有一个candidateName符合,它是我们的主配置类,也就是SpringbootApplication这个Bean。
- ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory);
-
- public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef,
- MetadataReaderFactory metadataReaderFactory) {
-
- String className = beanDef.getBeanClassName();
- // 获取下类名,如果类名为空或者该类为工厂类
- if (className == null || beanDef.getFactoryMethodName() != null) {
- return false;
- }
-
- // 获取类的元数据信息
- AnnotationMetadata metadata;
- // 上图的7个candidateNames中只有一个springbootApplication是AnnotatedBeanDefinition,其余全返回false
- if (beanDef instanceof AnnotatedBeanDefinition &&
- className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
- // Can reuse the pre-parsed metadata from the given BeanDefinition...
- // springbootApplication走到这里
- metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
- }
- else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
- // Check already loaded Class if present...
- // since we possibly can't even load the class file for this Class.
- Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
- metadata = new StandardAnnotationMetadata(beanClass, true);
- }
- else {
- try {
- MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
- // 读取类的元数据信息,这里面包括注解等信息
- metadata = metadataReader.getAnnotationMetadata();
- }
- catch (IOException ex) {
- return false;
- }
- }
-
- // metadata.isAnnotated(Configuration.class.getName()),这个就是判断类上面有没有@Configuration注解
- if (isFullConfigurationCandidate(metadata)) {
- // 如果true的话设置下这个属性,那么就标记为处理过了
- beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
- } else if (isLiteConfigurationCandidate(metadata)) {
- beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
- }
- else {
- // 其余6个返回false
- return false;
- }
-
- // It's a full or lite configuration candidate... Let's determine the order value, if any.
- Integer order = getOrder(metadata);
- if (order != null) {
- // 获取下类上的@Order信息
- beanDef.setAttribute(ORDER_ATTRIBUTE, order);
- }
- return true;
- }
- public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
- // Do not consider an interface or an annotation...
- if (metadata.isInterface()) {
- return false;
- }
-
- // Any of the typical annotations found?
- for (String indicator : candidateIndicators) {
- // 判断下类上面有没有这几个注解
- if (metadata.isAnnotated(indicator)) {
- return true;
- }
- }
-
- // Finally, let's look for @Bean methods...
- try {
- // 判断有没有@Bean的方法
- return metadata.hasAnnotatedMethods(Bean.class.getName());
- }
- return false;
- }
- }
-
- private static final Set<string> candidateIndicators = new HashSet<>();
-
- static {
- candidateIndicators.add(Component.class.getName());
- candidateIndicators.add(ComponentScan.class.getName());
- candidateIndicators.add(Import.class.getName());
- candidateIndicators.add(ImportResource.class.getName());
- }
- public void parse(Set<beandefinitionholder> configCandidates) {
- // 删除部分代码,实际执行时这里的configCandidates就一个springBootApplication代表的主配置类
- for (BeanDefinitionHolder holder : configCandidates) {
- // 获取BeanDefinition
- BeanDefinition bd = holder.getBeanDefinition();
- // 我们的SpringBootApplication会走到这边,下面先分析这边
- parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
- }
- // (**)处这边也要留意下,这边会处理DeferredImportSelector,我们前面说的AutoConfigurationImportSelector就是在这边处理
- // 给容器中导入xxxAutoConfiguration
- this.deferredImportSelectorHandler.process();
- }
-
- protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
- processConfigurationClass(new ConfigurationClass(metadata, beanName));
- }
-
- protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
- // 根据当前类上面的@Conditional注解标注的条件判断是否要解析这个配置类
- if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
- return;
- }
- // 以configClass作为key去获取,第一次来肯定是获取不到的,走下面逻辑
- ConfigurationClass existingClass = this.configurationClasses.get(configClass);
- if (existingClass != null) {
- if (configClass.isImported()) {
- if (existingClass.isImported()) {
- existingClass.mergeImportedBy(configClass);
- }
- // Otherwise ignore new imported config class; existing non-imported class overrides it.
- return;
- }
- else {
- // Explicit bean definition found, probably replacing an import.
- // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass);
- this.knownSuperclasses.values().removeIf(configClass::equals);
- }
- }
-
- // Recursively process the configuration class and its superclass hierarchy.
- // 这一步其实没有做啥,重点还是看下一步骤
- SourceClass sourceClass = asSourceClass(configClass);
- do {
- // 这里是重点,里面具体分为8大步骤,单独拿一小节分析
- // b) doProcessConfigurationClass
- sourceClass = doProcessConfigurationClass(configClass, sourceClass);
- }
- while (sourceClass != null);
- // 放入configurationClasses中
- this.configurationClasses.put(configClass, configClass);
- }
- // 上面的asSourceClass最终其实就是封装了一个SourceClass对象
- public SourceClass(Object source) {
- this.source = source;
- if (source instanceof Class) {
- this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);
- }
- else {
- this.metadata = ((MetadataReader) source).getAnnotationMetadata();
- }
- }
下面这个doProcessConfigurationClass具体分为8个小步骤去解析,对应步骤四种的A-H步骤
- protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
- throws IOException {
- // 判断这个类上面有没有@Component注解
- if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
- // Recursively process any member (nested) classes first
- // 如果有的话,遍历其内部类,然后也是调用doProcessConfigurationClass递归处理
- processMemberClasses(configClass, sourceClass);
- }
-
- // Process any @PropertySource annotations
- // 处理PropertySource注解,之前讲解属性配置也分析过,就是将该注解对应的属性文件加载到Environment中
- for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
- sourceClass.getMetadata(), PropertySources.class,
- org.springframework.context.annotation.PropertySource.class)) {
- if (this.environment instanceof ConfigurableEnvironment) {
- processPropertySource(propertySource);
- }
- }
-
- // Process any @ComponentScan annotations
- // 处理@ComponentScan注解,将其指定的包下的bean注册到框架中
- Set<annotationattributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
- sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
- if (!componentScans.isEmpty() &&
- !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
- for (AnnotationAttributes componentScan : componentScans) {
- // The config class is annotated with @ComponentScan -> perform the scan immediately
- Set<beandefinitionholder> scannedBeanDefinitions =
- this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
- // Check the set of scanned definitions for any further config classes and parse recursively if needed
- for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
- BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
- if (bdCand == null) {
- bdCand = holder.getBeanDefinition();
- }
- if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
- parse(bdCand.getBeanClassName(), holder.getBeanName());
- }
- }
- }
- }
-
- // Process any @Import annotations
- // 处理Import注解
- processImports(configClass, sourceClass, getImports(sourceClass), true);
-
- // Process any @ImportResource annotations
- // 处理@ImportResource注解,可以通过它来指定xml文件,BeanFactory就会读取这个xml文件将bean注册进去
- AnnotationAttributes importResource =
- AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
- if (importResource != null) {
- String[] resources = importResource.getStringArray("locations");
- Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
- for (String resource : resources) {
- String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
- configClass.addImportedResource(resolvedResource, readerClass);
- }
- }
-
- // Process individual @Bean methods
- // 处理我们的类中使用@Bean注解的方法,添加到configClass的beanMethod中
- Set<methodmetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
- for (MethodMetadata methodMetadata : beanMethods) {
- configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
- }
-
- // Process default methods on interfaces
- // 处理接口的默认方法,遍历这个类的接口,判断有没有使用@Bean注解的非抽象方法,添加到configClass的beanMethod中
- processInterfaces(configClass, sourceClass);
-
- // Process superclass, if any
- // 递归处理父类,这边返回父类上层方法会递归处理
- if (sourceClass.getMetadata().hasSuperClass()) {
- // 判断父类不为null且不在knownSuperclasses中且不以Java开头
- String superclass = sourceClass.getMetadata().getSuperClassName();
- if (superclass != null && !superclass.startsWith("java") &&
- !this.knownSuperclasses.containsKey(superclass)) {
- this.knownSuperclasses.put(superclass, configClass);
- // Superclass found, return its annotation metadata and recurse
- return sourceClass.getSuperClass();
- }
- }
-
- // No superclass -> processing is complete
- return null;
- }
- if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
- // Recursively process any member (nested) classes first
- processMemberClasses(configClass, sourceClass);
- }
-
-
- // Register member (nested) classes that happen to be configuration classes themselves.
- private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
- Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
- // 判断是否有内部类,没有的话直接不处理
- if (!memberClasses.isEmpty()) {
- List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
- for (SourceClass memberClass : memberClasses) {
- // 判断是否是配置类,判断也很简单,之前分析过,判断类上面有没有@Configuration注解、@Import、@ImportResource
- // @Component、@ComponentScan以及@Bean标注的方法
- if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
- !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
- // 加入到candidates中然后排个序
- candidates.add(memberClass);
- }
- }
- OrderComparator.sort(candidates);
- for (SourceClass candidate : candidates) {
- // 防止A引入防止A引入B,B引入A
- if (this.importStack.contains(configClass)) {
- this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
- }
- else {
- this.importStack.push(configClass);
- try {
- // 放入栈中并遍历处理这些配置类,也是递归处理,调用之前的doProcessConfigurationClass处理这个配置类
- processConfigurationClass(candidate.asConfigClass(configClass));
- }
- finally {
- this.importStack.pop();
- }
- }
- }
- }
- }
- @SpringBootApplication
- @PropertySource({"demo.properties"})
- public class Springboot2Application {
- for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
- sourceClass.getMetadata(), PropertySources.class,
- org.springframework.context.annotation.PropertySource.class)) {
- if (this.environment instanceof ConfigurableEnvironment) {
- // 这边就不进去看了,主要是读取@PropertySource注解指定的文件,将其封装成一个属性集放入到环境中
- processPropertySource(propertySource);
- }
- }
- Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
- sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
- if (!componentScans.isEmpty() &&
- !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
- for (AnnotationAttributes componentScan : componentScans) {
- // The config class is annotated with @ComponentScan -> perform the scan immediately
- // 下面先分析这个parse方法
- Set<BeanDefinitionHolder> scannedBeanDefinitions =
- this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
- // Check the set of scanned definitions for any further config classes and parse recursively if needed
- for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
- BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
- if (bdCand == null) {
- bdCand = holder.getBeanDefinition();
- }
- // 如果是配置类,再递归处理
- if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
- parse(bdCand.getBeanClassName(), holder.getBeanName());
- }
- }
- }
- }
- public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
- ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
- componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
-
- Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
- boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
- // 设置一个bean名字生成器,默认就是使用org.springframework.beans.factory.support.BeanNameGenerator
- scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
- BeanUtils.instantiateClass(generatorClass));
-
- // 就是默认的
- ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
- if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
- scanner.setScopedProxyMode(scopedProxyMode);
- }
- else {
- Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
- // 理解是元数据解析器
- scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
- }
-
- // 设置下扫描的资源模式,是**/*.class
- scanner.setResourcePattern(componentScan.getString("resourcePattern"));
-
- // 添加IncludeFilter和ExcludeFilter
- for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
- for (TypeFilter typeFilter : typeFiltersFor(filter)) {
- scanner.addIncludeFilter(typeFilter);
- }
- }
- for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
- for (TypeFilter typeFilter : typeFiltersFor(filter)) {
- scanner.addExcludeFilter(typeFilter);
- }
- }
- // 设置是否懒加载
- boolean lazyInit = componentScan.getBoolean("lazyInit");
- if (lazyInit) {
- scanner.getBeanDefinitionDefaults().setLazyInit(true);
- }
-
- // 解析扫描的包路径加入到basePackages中
- Set<String> basePackages = new LinkedHashSet<>();
- String[] basePackagesArray = componentScan.getStringArray("basePackages");
- for (String pkg : basePackagesArray) {
- String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
- ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
- Collections.addAll(basePackages, tokenized);
- }
- for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
- // 解析basePackageClasses所在的包并加入到basePackages
- basePackages.add(ClassUtils.getPackageName(clazz));
- }
- // 如果是空的,将声明该注解所在的类的包加入到basePackages
- if (basePackages.isEmpty()) {
- // 通常我们的主配置类是没有声明包扫描的路径的,所以这里会将主配置类所在的包加到这里面
- basePackages.add(ClassUtils.getPackageName(declaringClass));
- }
-
- // 添加一个ExcludeFilter,跳过声明该注解的类
- scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
- @Override
- protected boolean matchClassName(String className) {
- return declaringClass.equals(className);
- }
- });
- return scanner.doScan(StringUtils.toStringArray(basePackages));
- }
- protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
- Assert.notEmpty(basePackages, "At least one base package must be specified");
- Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
- // 遍历所有的包路径
- for (String basePackage : basePackages) {
- // 获取该包下面所有符合条件的BeanDefinition,然后遍历处理,下面会分析
- Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
- for (BeanDefinition candidate : candidates) {
- ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
- candidate.setScope(scopeMetadata.getScopeName());
- // 通过beanNameGenerator生成beanName
- String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
- // 这两个if判断逻辑比较简单,就是设置一些Lazy、DependsOn属性
- if (candidate instanceof AbstractBeanDefinition) {
- postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
- }
- if (candidate instanceof AnnotatedBeanDefinition) {
- AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
- }
- // 这边是检查下有没有之前定义过这个BeanDefinition
- if (checkCandidate(beanName, candidate)) {
- BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
- definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata,
- definitionHolder, this.registry);
- // 将该beanDefinition加入到集合中,并注册到容器中
- beanDefinitions.add(definitionHolder);
- registerBeanDefinition(definitionHolder, this.registry);
- }
- }
- }
- return beanDefinitions;
- }
- private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
- Set<BeanDefinition> candidates = new LinkedHashSet<>();
- try {
- // 扫描指定包路径及其子包下面的class文件,将其封装成Resource对象
- // classpath*:com/lwh/springboot/**/*.class
- String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
- resolveBasePackage(basePackage) + '/' + this.resourcePattern;
- Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
-
- for (Resource resource : resources) {
- if (resource.isReadable()) {
- try {
- MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
- if (isCandidateComponent(metadataReader)) {
- ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
- sbd.setResource(resource);
- sbd.setSource(resource);
- if (isCandidateComponent(sbd)) {
- candidates.add(sbd);
- }
- }
- }
- }
- }
- }
- return candidates;
- }
- // 通过之前设置的几个filter进行过滤
- protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
- for (TypeFilter tf : this.excludeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return false;
- }
- }
- for (TypeFilter tf : this.includeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return isConditionMatch(metadataReader);
- }
- }
- return false;
- }
- // Process any @Import annotations
- // getImports方法就是去递归扫描configClass上面所有的注解,将@Import注解标注的值放入importCandidates中,见下图
- processImports(configClass, sourceClass, getImports(sourceClass), true);
- private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
- Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
-
- if (importCandidates.isEmpty()) {
- return;
- }
-
- if (checkForCircularImports && isChainedImportOnStack(configClass)) {
- this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
- }
- else {
- this.importStack.push(configClass);
- try {
- for (SourceClass candidate : importCandidates) {
- // 依次遍历判断类型
- // 其中有一个是这个类型,@Import(AutoConfigurationImportSelector.class)
- // 这个就是自动配置原理,导入xxxAutoConfiguration这些类
- if (candidate.isAssignable(ImportSelector.class)) {
- // Candidate class is an ImportSelector -> delegate to it to determine imports
- Class<?> candidateClass = candidate.loadClass();
- // 实例化并调用xxxAware的方法并注入相关属性
- ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
- ParserStrategyUtils.invokeAwareMethods(
- selector, this.environment, this.resourceLoader, this.registry);
- // 它是DeferredImportSelector类型的
- if (selector instanceof DeferredImportSelector) {
- // deferredImportSelectors = new ArrayList<>()
- // 这边会将两个参数封装下加入到deferredImportSelectors中,后面处理
- // 加入到deferredImportSelectors中后,具体的处理是this.deferredImportSelectorHandler.process();
- this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
- }
- else {
- // 不是的话获取@Import导入的类名数组
- String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
- Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
- // 然后再递归处理
- processImports(configClass, currentSourceClass, importSourceClasses, false);
- }
- }
- // @Import(AutoConfigurationPackages.Registrar.class),我们的主配置类上面的注解就是这个类型
- // 这个是用于导入主配置类所在包及其子包下的BeanDefinition
- else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
- // Candidate class is an ImportBeanDefinitionRegistrar ->
- // delegate to it to register additional bean definitions
- Class<?> candidateClass = candidate.loadClass();
- ImportBeanDefinitionRegistrar registrar =
- BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
- ParserStrategyUtils.invokeAwareMethods(
- registrar, this.environment, this.resourceLoader, this.registry);
- // 这边就是将这两个参数作为key,value放入了一个map中
- // this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
- configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
- }
- else {
- // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
- // process it as an @Configuration class
- this.importStack.registerImport(
- currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
- // 当做一个普通类处理,判断是不是配置类,递归处理
- processConfigurationClass(candidate.asConfigClass(configClass));
- }
- }
- }
- finally {
- this.importStack.pop();
- }
- }
- }
- // 这种就是Spring中常用的通过XML形式注入的方式
- @SpringBootApplication
- @ImportResource("test.xml")
- public class Springboot2Application {
- // Process any @ImportResource annotations
- // 可以使用@ImportResource注解指定xml文件,导入BeanDefinition
- AnnotationAttributes importResource =
- AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
- if (importResource != null) {
- String[] resources = importResource.getStringArray("locations");
- Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
- for (String resource : resources) {
- // 就是test.xml
- String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
- configClass.addImportedResource(resolvedResource, readerClass);
- }
- }
-
- public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
- // 这边就是放入到了map中,这边是先统一存放起来,在步骤五的4)在真正进行导入BeanDefinition
- this.importedResources.put(importedResource, readerClass);
- }
-
- private final Map<String, Class<? extends BeanDefinitionReader>> importedResources = new LinkedHashMap<>();
- // Process individual @Bean methods
- // 获取当前类中的Bean方法
- Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
- for (MethodMetadata methodMetadata : beanMethods) {
- // 这边也是加入到set中,见下面代码,也是在步骤五的3)中进行处理
- configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
- }
-
- private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
- // 默认方法举例,主配置类实现这个接口就可以
- public interface ConfigurationInterface {
-
- @Bean
- default Pig pig(){
- return new Pig();
- }
- }
- // Process default methods on interfaces
- processInterfaces(configClass, sourceClass);
-
- // Register default methods on interfaces implemented by the configuration class.
- // 这边也是递归处理其父接口,判断父接口中默认方法是不是@Bean方法
- private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
- for (SourceClass ifc : sourceClass.getInterfaces()) {
- Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
- for (MethodMetadata methodMetadata : beanMethods) {
- if (!methodMetadata.isAbstract()) {
- // 也是在步骤五的3)中进行处理
- configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
- }
- }
- processInterfaces(configClass, ifc);
- }
- }
更多内容请留意公众号“vivo互联网技术”
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。