本篇文章为大家展示了Bean的解析与注册是怎样的,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
创新互联服务项目包括腾冲网站建设、腾冲网站制作、腾冲网页制作以及腾冲网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,腾冲网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到腾冲省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
我们将一起探讨spring对bean解析,并注册到IOC容器的过程。
我们先接着看下面这段代码:
ClassPathBeanDefinitionScanner类
//类路径Bean定义扫描器扫描给定包及其子包 protected SetdoScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); //创建一个集合,存放扫描到的BeanDefinition封装类 Set beanDefinitions = new LinkedHashSet<>(); //遍历扫描所有给定的包路径 for (String basePackage : basePackages) { //调用父类ClassPathScanningCandidateComponentProvider的方法 //扫描给定类路径,获取符合条件的Bean定义 10 Set candidates = findCandidateComponents(basePackage); //遍历扫描到的Bean for (BeanDefinition candidate : candidates) { //获取@Scope注解的值,即获取Bean的作用域 14 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); //为Bean设置作用域 candidate.setScope(scopeMetadata.getScopeName()); //为Bean生成名称 18 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); //如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值, //设置Bean的自动依赖注入装配属性等 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解 if (candidate instanceof AnnotatedBeanDefinition) { //处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突 30 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); //根据注解中配置的作用域,为Bean应用相应的代理模式 33 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //向容器注册扫描到的Bean 37 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
上篇文章我们主要分析了第10行的 findCandidateComponents(basePackage)
方法, 该方法主要是从给定的包路径中扫描符合过滤规则的Bean,并存入集合beanDefinitions
中。
接下来的步骤可以分为以下几个方面:
遍历bean集合
获取@Scope注解的值,即获取Bean的作用域
为Bean生成名称
给Bean的一些属性设置默认值
检查Bean是否已在IOC容器中注册
根据Bean的作用域,生成相应的代理模式
把Bean放入IOC容器中
首先来看下 获取Bean作用域的过程,主要是上面第14行ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
这段代码,我们继续跟踪进去:
AnnotationScopeMetadataResolver类
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) { //默认是singleton ScopeMetadata metadata = new ScopeMetadata(); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition; //获取@Scope注解的值 AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor( annDef.getMetadata(), this.scopeAnnotationType); //将获取到的@Scope注解的值设置到要返回的对象中 if (attributes != null) { metadata.setScopeName(attributes.getString("value")); //获取@Scope注解中的proxyMode属性值,在创建代理对象时会用到 ScopedProxyMode proxyMode = attributes.getEnum("proxyMode"); //如果@Scope的proxyMode属性为DEFAULT或者NO if (proxyMode == ScopedProxyMode.DEFAULT) { //设置proxyMode为NO proxyMode = this.defaultProxyMode; } //为返回的元数据设置proxyMode metadata.setScopedProxyMode(proxyMode); } } //返回解析的作用域元信息对象 return metadata; }
Bean的作用域是通过@Scope
注解实现的,我们先来看下@Scope
注解的属性:
可以看到@Scope
注解有三个属性,
value
属性就是我们常用的用来设置Bean的单例/多例
proxyMode
是用来设置代理方式的
关于@Scope
注解原理的详细分析,请看这篇文章<>,这里就不详细讲了。
这里的AnnotationAttributes
是注解属性key-value的封装类,继承了LinkedHashMap
,所以也是key-value形式的数据结构。 通过debug看一下,这个变量拿到的值:
可以看到获取到了3个属性的值,其中value = prototype
就是该Bean的作用域,这里我设置的是多例,然后再把获取到的注解属性值赋值给ScopeMetadata
。
再回到上面ClassPathBeanDefinitionScanner类的doScan()方法中的第18行, 把我们获取到的Bean的作用域赋值给Bean。
然后为Bean生成名字,代码String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
跟踪进去,在AnnotationBeanNameGenerator类中:
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { if (definition instanceof AnnotatedBeanDefinition) { 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); }
这段代码很好理解,先从注解中获取Bean的名称,如果注解中没有设置,那么生成一个默认的Bean的名称,通过ClassUtils.getShortName(beanClassName)
生成一个类名的小写开头驼峰的名字,如studentController
。
主要是doScan()中的如下两个方法:
//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值, //设置Bean的自动依赖注入装配属性等 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解 if (candidate instanceof AnnotatedBeanDefinition) { //处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); }
首先看postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName)
:
跟踪进去会到如下方法:
public void applyDefaults(BeanDefinitionDefaults defaults) { //懒加载 setLazyInit(defaults.isLazyInit()); //加载模式 setAutowireMode(defaults.getAutowireMode()); //依赖检查 setDependencyCheck(defaults.getDependencyCheck()); //bean初始化方法 setInitMethodName(defaults.getInitMethodName()); setEnforceInitMethod(false); //bean销毁方法 setDestroyMethodName(defaults.getDestroyMethodName()); setEnforceDestroyMethod(false); }
这里应该很清晰了,给bean设置一些默认值,BeanDefinitionDefaults
是一个Bean属性默认值的封装类,从该类获取各个属性的默认值,赋值给bean。
接着我们看AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)
方法。
跟踪进去:
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); //如果Bean定义中有@Lazy注解,则将该Bean预实例化属性设置为@lazy注解的值 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")); } } //如果Bean定义中有@Primary注解,则为该Bean设置为autowiring自动依赖注入装配的首选对象 if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } //如果Bean定义中有@ DependsOn注解,则为该Bean设置所依赖的Bean名称, //容器将确保在实例化该Bean之前首先实例化所依赖的Bean AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } if (abd instanceof AbstractBeanDefinition) { AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd; AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null) { absBd.setRole(role.getNumber("value").intValue()); } AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null) { absBd.setDescription(description.getString("value")); } } }
这里主要是处理bean上一些常用的注解,如@Lazy、@Primary、@DependsOn
。
注释很清晰,这里就不赘言了。
跟踪doScan()中的第30行if (checkCandidate(beanName, candidate))
方法:
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { //是否包含beanName了 if (!this.registry.containsBeanDefinition(beanName)) { return true; } //如果容器中已经存在同名bean //获取容器中已存在的bean BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } //新bean旧bean进行比较 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() + "]"); }
可以看到,其实是通过调用IOC容器的containsBeanDefinition(beanName)
方法,来判断该beanName是否已存在,而IOC容器实际上是一个map
,这里底层其实就是通过调用map.containsKey(key)
来实现的。
跟踪doScan()中的 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
方法
static BeanDefinitionHolder applyScopedProxyMode( ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) { //获取注解Bean定义类中@Scope注解的proxyMode属性值 ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode(); //如果配置的@Scope注解的proxyMode属性值为NO,则不应用代理模式 if (scopedProxyMode.equals(ScopedProxyMode.NO)) { return definition; } //获取配置的@Scope注解的proxyMode属性值,如果为TARGET_CLASS //则返回true,如果为INTERFACES,则返回false boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); //为注册的Bean创建相应模式的代理对象 return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); }
这里就用到第二步中我门获取到的@Scope注解的proxyMode
属性,然后为bean设置代理模式。
跟踪doScan()中的第37行registerBeanDefinition(definitionHolder, this.registry);
方法
//将解析的BeanDefinitionHold注册到容器中 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //获取解析的BeanDefinition的名称 String beanName = definitionHolder.getBeanName(); //向IOC容器注册BeanDefinition 第9行 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //如果解析的BeanDefinition有别名,向容器为其注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
直接看第9行的代码,继续跟踪进去:
//向IOC容器注册解析的BeanDefiniton @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校验 beanName 与 beanDefinition 非空 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //校验解析的BeanDefiniton if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; // 从容器中获取指定 beanName 的 BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 如果已经存在 if (oldBeanDefinition != null) { // 如果存在但是不允许覆盖,抛出异常 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } // 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志 else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } // 允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中。 this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 检测创建 Bean 阶段是否已经开启,如果开启了则需要对 beanDefinitionMap 进行并发控制 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) //注册的过程中需要线程同步,以保证数据的一致性(因为有put、add、remove操作) 64 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); ListupdatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; // 从 manualSingletonNames 移除 beanName if (this.manualSingletonNames.contains(beanName)) { Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } //检查是否有同名的BeanDefinition已经在IOC容器中注册 88 if (oldBeanDefinition != null || containsSingleton(beanName)) { //更新beanDefinitionNames 和 manualSingletonNames resetBeanDefinition(beanName); } }
这里就是向IOC容器中注册bean的核心代码,这段代码很长,我们分开来看,主要分为几个步骤:
beanName
和beanDefinition
的合法性校验
根据beanName
从IOC容器中判断是否已经注册过
根据isAllowBeanDefinitionOverriding
变量来判断是否覆盖
如果存在根据覆盖规则,执行覆盖或者抛出异常
如果不存在,则put到IOC容器beanDefinitionMap
中private final Map
到这里,注册bean到IOC容器的过程就基本结束了,实际上IOC注册不是什么神秘的东西,说白了就是把beanName
和bean
存入map集合中
此时我们再返回看第七步的代码BeanDefinitionReaderUtils
类的registerBeanDefinition()
方法,可以看到 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
我们已经分析完了,剩下的就是把bean的别名也注册进去就大功告成了。
//将解析的BeanDefinitionHold注册到容器中 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //获取解析的BeanDefinition的名称 String beanName = definitionHolder.getBeanName(); //向IOC容器注册BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //如果解析的BeanDefinition有别名,向容器为其注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
IoC容器其实就是DefaultListableBeanFactory
,它里面有一个map
类型的beanDefinitionMap
变量,来存储注册的bean
IoC容器初始化过程:
1、资源定位
扫描包路径下.class
文件,将资源转为Resource
2、资源加载
通过ASM框架获取class元数据,封装到BeanDefinition
3、资源解析
获取bean上注解的属性值。如@Scope
4、生成Bean
生成beanName
,设置Bean默认值(懒加载、初始化方法等)、代理模式
5、注册Bean
把BeanDefinition
放入IoC容器DefaultListableBeanFactory
最后思考几个小疑问:
beanDefinitionMap
是ConcurrentHashMap
类型的,应该是线程安全的,但是为什么在代码64行的地方,还要加sync锁呢?
之前已经检查过容器中是否有重名bean了,为什么在88行还要再检查一次呢?
上述内容就是Bean的解析与注册是怎样的,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注创新互联行业资讯频道。
售后响应及时
7×24小时客服热线数据备份
更安全、更高效、更稳定价格公道精准
项目经理精准报价不弄虚作假合作无风险
重合同讲信誉,无效全额退款