一文看懂@Bean注解的原理
在spring中可以通过配置类的标注@Bean注解的方法生成bean,这是常见的创建bean的方式之一,用法很简单,那他的原理是怎样的呢。首先处理配置类检测到方法存在@Bean注解时,就会提取bean方法。先反射获取获取bean方法,再asm获取bean方法,asm处理class就是处理原始的class字节码文件,然后把反射获取的bean方法按照asm获取的进行排序,目的是保证bean方法的有序性,因为有时候如果bean方法存在条件注解,需要先解析没有条件注解的bean方法,才能让条件生效。
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {AnnotationMetadata original = sourceClass.getMetadata();//反射获取bean方法Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {try {AnnotationMetadata asm =this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();//asm获取bean方法Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());//按照asm获取顺序进行排序if (asmMethods.size() >= beanMethods.size()) {Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());for (MethodMetadata asmMethod : asmMethods) {for (MethodMetadata beanMethod : beanMethods) {if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {selectedMethods.add(beanMethod);break;}}}if (selectedMethods.size() == beanMethods.size()) {// All reflection-detected methods found in ASM method set -> proceedbeanMethods = selectedMethods;}}}catch (IOException ex) {logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);}}return beanMethods;}
然后开始根据@Bean注解生成bean定义注册到容器中,这个过程有点复杂。
- 根据条件注解判断bean是否需要处理,如果不需要处理,则重载的同名bean方法也无需处理
- 处理同名bean方法
- 创建bean定义注册到容器中,设置bean的反射调用方式、代理模式等
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {ConfigurationClass configClass = beanMethod.getConfigurationClass();MethodMetadata metadata = beanMethod.getMetadata();String methodName = metadata.getMethodName();//根据条件注解标记当前配置类的bean方法是否跳过if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {configClass.skippedBeanMethods.add(methodName);return;}//被标记跳过的bean方法无需处理if (configClass.skippedBeanMethods.contains(methodName)) {return;}AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);Assert.state(bean != null, "No @Bean annotation attributes");// Consider name and any aliasesList<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));String beanName = (!names.isEmpty() ? names.remove(0) : methodName);// Register aliases even when overriddenfor (String alias : names) {this.registry.registerAlias(beanName, alias);}// 发现同名bean方法的处理if (isOverriddenByExistingDefinition(beanMethod, beanName)) {if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +"' clashes with bean name for containing configuration class; please make those names unique!");}return;}//创建bean定义注册到容器中ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));//static方法根据class调用if (metadata.isStatic()) {// static @Bean methodif (configClass.getMetadata() instanceof StandardAnnotationMetadata) {beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());}else {beanDef.setBeanClassName(configClass.getMetadata().getClassName());}beanDef.setUniqueFactoryMethodName(methodName);}//实例方法根据name获取到实力对象再反射调用else {// instance @Bean methodbeanDef.setFactoryBeanName(configClass.getBeanName());beanDef.setUniqueFactoryMethodName(methodName);}if (metadata instanceof StandardMethodMetadata) {beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());}beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);//处理@Lazy、@Primary、@DependsOn、@Role等注解AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);Autowire autowire = bean.getEnum("autowire");if (autowire.isAutowire()) {beanDef.setAutowireMode(autowire.value());}boolean autowireCandidate = bean.getBoolean("autowireCandidate");if (!autowireCandidate) {beanDef.setAutowireCandidate(false);}String initMethodName = bean.getString("initMethod");if (StringUtils.hasText(initMethodName)) {beanDef.setInitMethodName(initMethodName);}String destroyMethodName = bean.getString("destroyMethod");beanDef.setDestroyMethodName(destroyMethodName);// Consider scopingScopedProxyMode proxyMode = ScopedProxyMode.NO;AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);if (attributes != null) {beanDef.setScope(attributes.getString("value"));proxyMode = attributes.getEnum("proxyMode");if (proxyMode == ScopedProxyMode.DEFAULT) {proxyMode = ScopedProxyMode.NO;}}// 是否生成代理对象BeanDefinition beanDefToRegister = beanDef;if (proxyMode != ScopedProxyMode.NO) {BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(new BeanDefinitionHolder(beanDef, beanName), this.registry,proxyMode == ScopedProxyMode.TARGET_CLASS);beanDefToRegister = new ConfigurationClassBeanDefinition((RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);}if (logger.isTraceEnabled()) {logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",configClass.getMetadata().getClassName(), beanName));}this.registry.registerBeanDefinition(beanName, beanDefToRegister);}
遇到同名bean时,处理的过程就是不存在bean定义就创建;配置类相同,bean方法名相同,则标记为方法不唯一,无须再次处理,之后创建bean的时候会调用多次。
protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {//不存在bean定义就创建if (!this.registry.containsBeanDefinition(beanName)) {return false;}BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;if (ccbd.getMetadata().getClassName().equals(beanMethod.getConfigurationClass().getMetadata().getClassName())) {if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {//配置类相同,bean方法名相同,则标记为方法不唯一,无须再次处理ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());}return true;}else {return false;}}//同名的bean是注解创建的,则覆盖if (existingBeanDef instanceof ScannedGenericBeanDefinition) {return false;}// Has the existing bean definition bean marked as a framework-generated bean?// -> allow the current bean method to override it, since it is application-levelif (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {return false;}// 容器不允许覆盖bean定义抛出异常if (this.registry instanceof DefaultListableBeanFactory &&!((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);}if (logger.isDebugEnabled()) {logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +"already exists. This top-level bean definition is considered as an override.",beanMethod, beanName));}return true;}
总结下,spring处理@Bean方法时会先判断条件是否跳过,再处理同名方法,设置bean的反射调用方式以及代理模式。有不对的地方请大神指出,欢迎大家一起讨论交流,共同进步,更多请关注微信公众号 葡萄开源