前言
本节将正式介绍Spring源码细节,将讲解Bean生命周期。请注意,虽然我们不希望过于繁琐地理解Spring源码,但也不要认为Spring源码很简单。在本节中,我们将主要讲解Spring 5.3.10版本的源代码。如果您看到的代码与我讲解的不同,也没有关系,因为其中的原理和业务逻辑基本相同。为了更好地理解,我们将先讲解Bean的生命周期,再讲解Spring的启动原理和流程,因为启动是准备工作的一部分。
题外话
目前在该版本中,引入了一个名为jfr的JDK技术,类似于Java飞行日志(JFL),也称为飞行数据记录器(Black Box)技术。具体作用不再详细阐述,读者可以参考此文:JFR介绍
如果您看到以下代码,请直接跳过,因为它并没有太大的作用:
public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); // 额外会创建StandardEnvironment this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); }
需要注意的是,其中的 StartupStep
关于默认实现并没有什么实际作用。但是,还有一种实现方式是 FlightRecorderStartupStep
,它是JDK的JFR技术。
Bean的生成过程生成BeanDefinition
BeanDefinition的作用大家基本通过前面的文章也知道了大概,就是用来描述bean的。
那么它是如何加载的呢?首先我们看一下 ClassPathBeanDefinitionScanner
类,它是用于扫描的。其中有一个属性是 BeanDefinitionRegistry
,即Bean定义的注册类。默认实现是 DefaultListableBeanFactory
,但是在 ClassPathBeanDefinitionScanner
类中并没有直接使用该类作为属性,而是使用了它的父接口 BeanDefinitionRegistry
。这是因为 ClassPathBeanDefinitionScanner
类实际上并没有使用 BeanDefinitionRegistry
接口中的许多方法来注册Bean定义。
接下来,我们来分析 ClassPathBeanDefinitionScanner
类的 scan
方法:
protected Set doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet(); for (String basePackage : basePackages) { Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { // 解析@Lazy、@Primary、@DependsOn、@Role、@Description AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 检查Spring容器中是否已经存在该beanName if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注册 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
大致逻辑如下:
- 获取扫描包路径
- findCandidateComponents:获取符合条件的bean
- 遍历candidate(候选bean),由于第二部使用了ASM技术,所以并没有真正获取beanclass而是使用了beanname替代所以,遍历的做法就是将符合条件的bean定义进行注册。
- scopeMetadata解析scope注解。
- beanNameGenerator构建当前bean的唯一名字。
- postProcessBeanDefinition这里其实就是进行默认值赋值。
- processCommonDefinitionAnnotations进行解析@Lazy、@Primary、@DependsOn、@Role、@Description
- checkCandidate(beanName, candidate)再次检查是否该beanName已经注册过。
- registerBeanDefinition,注册到我们的DefaultListableBeanFactory的BeanDefinitionMap中。
其实这里基本就已经大概了解 的差不多了,然后再继续讲解下每一个流程里面都走了那些逻辑:
findCandidateComponents
其主要逻辑会进入如下源码:
private Set scanCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet(); try { // 获取basePackage下所有的文件资源 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // excludeFilters、includeFilters判断 if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } //此处省略部分代码 ...... } return candidates; }
让我们来深入了解一下Bean扫描的具体细节。以下是主要流程:
- 获取basePackage下所有的文件资源。以下分析注解信息等都是用到了ASM技术,并没有真正的去加载这个类。
- isCandidateComponent(metadataReader),进行判断是否当前类具有@component注解。
- isCandidateComponent(sbd),进行判断是否当前类属于内部类、接口、抽象类
- 符合上述条件则会加入到bean定义候选集合中。
ASM技术这里不做多解释,主要看下Spring是如何进行判断校验当前bean是否符合条件的,第一个 isCandidateComponent(metadataReader)方法:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}// 符合includeFilters的会进行条件匹配,通过了才是Bean,也就是先看有没有@Component,再看是否符合@Conditionalfor (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}
那么includeFilters默认会在启动AnnotationConfigApplicationContext时就会默认注册一个解析Component注解的filter,代码如下:
protected void registerDefaultFilters() { // 注册@Component对应的AnnotationTypeFilter this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
如果符合过滤条件,那么他就会开始生成最初的bean定义:
public ScannedGenericBeanDefinition(MetadataReader metadataReader) { Assert.notNull(metadataReader, "MetadataReader must not be null"); this.metadata = metadataReader.getAnnotationMetadata(); // 这里只是把className设置到BeanDefinition中 setBeanClassName(this.metadata.getClassName()); setResource(metadataReader.getResource()); }
那么就剩下最后的校验了:isCandidateComponent(sbd);再来看看他的作用子什么:
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { AnnotationMetadata metadata = beanDefinition.getMetadata(); return (metadata.isIndependent() && (metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())))); }
看完一脸懵,那我们就好好解释一下每个判断都是什么意思吧:
- metadata.isIndependent():是否当前类为内部类,众说周知java语言编译内部类的时候会产生两个class文件,比如下面这样:
那么这个Member内部类是不会被Spring作为单独的类去扫描的。除非也加上@component注解,并且为static内部类 - metadata.isConcrete():这个类就是判断下是否是接口还是抽象类
- metadata.hasAnnotatedMethods(Lookup.class.getName()):判断是否有方法是带有Lookup注解的,Lookup注解工作中用到的确实有些少,我在这里简要说明下:比如我们注册给Spring的bean都是单例,但是如果我们有多例的bean被一个单例的bean所依赖的话,一次属性只能注入一次,也打不到多例的效果,这时候就可以用Lookup注解实现了,比如这样:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); //UserService 为单例UserService bean = applicationContext.getBean(UserService.class); bean.test(); bean.test();
@Component public class UserService { @Autowired private User user; public void test(){ System.out.println(user); } }
@Component @Scope("prototype") public class User { }
如果这样执行的话,你永远拿不到多例的User类,因为UserService 在属性依赖注入的时候已经做完了赋值,每次调用拿到的都是同一个对象,那如果不这么做可不可以,当然可以:比如这样改下UserService 类:
@Component public class UserService { @Autowired private User user; public void test(){ System.out.println(get()); } @Lookup public User get(){ return null; } }
至于为什么这里返回null,后续在进行讲解,只要知道他的注解的作用即可。到此我们终于解决完了findCandidateComponents方法找出了符合条件的bean定义。
generateBeanName
获取当前bean的beanname,源码如下:
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { if (definition instanceof AnnotatedBeanDefinition) { // 获取注解所指定的beanName String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); if (StringUtils.hasText(beanName)) { // Explicit bean name found. return beanName; } } // Fallback: generate a unique default bean name. return buildDefaultBeanName(definition, registry); }
buildDefaultBeanName构建注解我们写 beanname这个不难理解,如果没有那么就会走默认的构建,那么这里是jdk提供的,有个点需要注意下如果首字母和第二个字母都是大写那么名字直接return,如果你写的是ABtest,那么beanname就是ABtest:
public static String decapitalize(String name) { if (name == null || name.length() == 0) { return name; } if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){ return name; } char chars[] = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); }
postProcessBeanDefinition
这里主要是进行设置BeanDefinition的默认值,直接看源码就能看懂:
public void applyDefaults(BeanDefinitionDefaults defaults) { Boolean lazyInit = defaults.getLazyInit(); if (lazyInit != null) { setLazyInit(lazyInit); } setAutowireMode(defaults.getAutowireMode()); setDependencyCheck(defaults.getDependencyCheck()); setInitMethodName(defaults.getInitMethodName()); setEnforceInitMethod(false); setDestroyMethodName(defaults.getDestroyMethodName()); setEnforceDestroyMethod(false); }
processCommonDefinitionAnnotations
这一步主要时进行解析类上的注解,看源码也可以基本看懂,没有太多绕的逻辑,如下展示:
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } else if (abd.getMetadata() != metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } } if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null) { abd.setRole(role.getNumber("value").intValue()); } AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null) { abd.setDescription(description.getString("value")); } }
checkCandidate
这一步主要是检查是否我们的bean定义map注册中已经存在了,不过我们工作中基本上都会通过,如果存在多个那会抛异常:
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { if (!this.registry.containsBeanDefinition(beanName)) { return true; } BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } // 是否兼容,如果兼容返回false表示不会重新注册到Spring容器中,如果不冲突则会抛异常。 if (isCompatible(beanDefinition, existingDef)) { return false; } throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " + "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); }
可是你像我这样下面写的话就不会出现异常,但是工作中肯定也不会这么用,这里只做展示,AppConfig和AppConfig1都是对同样的配置,会对同一个包路径扫描两次:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(AppConfig.class); applicationContext.register(AppConfig1.class); applicationContext.refresh(); UserService bean = applicationContext.getBean(UserService.class); bean.test();
registerBeanDefinition
那么最后当前的bean定义正式生成,并注册到我们之前经常说的DefaultListableBeanFactory的Map beanDefinitionMap属性。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
合并bean定义
这个是非常重要的一个步骤,所以单独拿出来说下,这个步骤也是获取bean之前的最后一个对bean定义做修改的地方,getMergedLocalBeanDefinition(beanName);通过beanname来获取合并后的bean定义,这是什么意思呢?看下源码:
protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; RootBeanDefinition previous = null; // Check with full lock now in order to enforce the same merged instance. if (containingBd == null) { mbd = this.mergedBeanDefinitions.get(beanName); } if (mbd == null || mbd.stale) { previous = mbd; if (bd.getParentName() == null) { // Use copy of given root bean definition. if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } else { // Child bean definition: needs to be merged with parent. // pbd表示parentBeanDefinition BeanDefinition pbd; try { String parentBeanName = transformedBeanName(bd.getParentName()); if (!beanName.equals(parentBeanName)) { pbd = getMergedBeanDefinition(parentBeanName); } else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { ...... } } } catch (NoSuchBeanDefinitionException ex) { ...... } // Deep copy with overridden values. // 子BeanDefinition的属性覆盖父BeanDefinition的属性,这就是合并 mbd = new RootBeanDefinition(pbd); mbd.overrideFrom(bd); } // Set default singleton scope, if not configured before. if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(SCOPE_SINGLETON); } if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } if (previous != null) { copyRelevantMergedBeanDefinitionCaches(previous, mbd); } return mbd; } }
为什么需要进行合并bean定义,因为每个bean都会可能被其他声明bean所引用,因为我们在工作中用到的都是注解形式,所以很少注意,我们看下Spring是xml时代时写的声明bean:
然后我们在看下合并bean定义的源码逻辑:
- 判断是否有parent,没有的话就正常包装原始bean定义为RootBeanDefinition。
- 如果有parent,那么在判断其父类是否还有父类,如果有则递归合并bean定义方法。
- 最关键的其实是这个代码:先以父类的bean定义生成RootBeanDefinition,然后如果子类定义了某个属性的话那就覆盖父类的bean定义。
mbd = new RootBeanDefinition(pbd); mbd.overrideFrom(bd);
public void overrideFrom(BeanDefinition other) { if (StringUtils.hasLength(other.getBeanClassName())) { setBeanClassName(other.getBeanClassName()); } if (StringUtils.hasLength(other.getScope())) { setScope(other.getScope()); } setAbstract(other.isAbstract()); if (StringUtils.hasLength(other.getFactoryBeanName())) { setFactoryBeanName(other.getFactoryBeanName()); } if (StringUtils.hasLength(other.getFactoryMethodName())) { setFactoryMethodName(other.getFactoryMethodName()); } setRole(other.getRole()); setSource(other.getSource()); copyAttributesFrom(other); //此处省略部分代码 ......}
- 最后将包装的bean定义放入mergedBeanDefinitions合并定义的map中。
现在我们的的Spring中 了两个bean定义Map,那么在启动时进行创建bean时用到的都是合并后的bean定义map。
结语
那么现在Spring的Bean定义环节就基本讲解完毕了。其实最主要的是Spring是如何判断是否将Bean定义加入,并生成Bean定义,以及最后如何使用合并的Bean定义来包装原始的Bean定义。下一节我们将开始讲解Spring的Bean实例化。
ps:以上内容,纯属个人见解,有任何问题下方评论!关注博主公众号,源码专题、面试精选、AI最新扩展等你来看!原创编写不易,转载请说明出处!