0102Bean配置和解析-Bean生命周期-spring

news2025/7/16 22:05:18

文章目录

    • 1 前言
    • 2 第一阶段-Bean信息配置阶段
      • 2.1 配置方式
      • 2.2 配置信息
      • 2.3 配置示例
        • 2.3.1 注解方式配置
        • 2.3.2 xml文件配置
        • 2.3.3 API方式
    • 3 第二阶段-Bean元信息解析阶段
      • 3.1 注解方式解析
        • 3.1.1 AnnotatedBeanDefinitionReader解析过程
        • 3.1.2 ClassPathBeanDefinitionScanner解析过程
      • 3.2 xml文件解析
      • 3.3 注解解析与xml解析比较
    • 4 问题解答
    • 后记

1 前言

根据配置Bean方式不同,在定义域解析注册阶段,会执行不同的流程。下面给出根据不同配置方式,执行的不同流程图:

在这里插入图片描述

2 第一阶段-Bean信息配置阶段

2.1 配置方式

Bean信息配置(定义)有4种方式:

  • 注解方式:@Component、@Configuration@Bean等等
  • xml文件方式:xxx.xml
  • API方式:通过BeanDefinitionBuilder工具类提供的各种API完成
  • properties文件方式:不常用,这里不详述,感兴趣的自行查阅相关文档。

2.2 配置信息

具体可以配置那些信息,在下面API配置打印BeanDefiniton信息后,我们在详细说明。

2.3 配置示例

下面给出一个简单的应用场景,现在我们需要把一个User(用户)信息存入数据库,按照三层架构,这里省略Controller,只给出UserService和UserDao。分别通过不同的方式配置UserDao和UserService的bean。

User类:

public class User {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

2.3.1 注解方式配置

  • UserDao

    @Repository
    public class UserDao {
        public void insert(User user) {
            System.out.println("数据库正在插入数据!: " + user);
        }
    }
    
  • UserService

    @Service
    public class UserService {
    
        @Autowired
        private UserDao userDao;
    
        public void insert(User user) {
            userDao.insert(user);
        }
    }
    

2.3.2 xml文件配置

在UserService需要给属性userDao添加set方法

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

xml文件放置在类路径resources下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.gaogzhen.myspring.dao.UserDao"/>

    <bean id="userService" class="com.gaogzhen.myspring.service.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

2.3.3 API方式

这里只做简单示范,在讲解完BeanDefinition注册后,单独讲解API方式详细配置,包括设置简单类型,设置引用类型,设置parent等等。

    public void testApiConfig() {
        BeanDefinitionBuilder userDaoBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserDao.class);
        Definition beanDefinition = userDaoBuilder.getBeanDefinition();

        System.out.println("userDao 的配置信息:" + beanDefinition);
        BeanDefinitionBuilder userServiceBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class)
                .addPropertyReference("userDao", "userDao");
       BeanDefinition serviceBeanDefinition = userServiceBuilder.getBeanDefinition();
        System.out.println("userService 的配置信息:" + serviceBeanDefinition);
    }

输出信息如下:

userDao 的配置信息:Generic bean: class [com.gaogzhen.myspring.dao.UserDao]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
userService 的配置信息:Generic bean: class [com.gaogzhen.myspring.service.UserService]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null

bean可配置信息:

  • class:类
  • scope:作用域,单例或者多例;在web应用中,还可以配置request,session等。
  • abstract:是否是抽象的
  • lazyInit:是否懒惰初始化
  • autowireMode:自动注入模式
    • AUTOWIRE_NO:不自动注入
    • AUTOWIRE_BY_NAME:通过bean名称自动注入(需要提供set方法)
    • AUTOWIRE_BY_TYPE:通过类型自动注入(需要提供set方法)
    • AUTOWIRE_CONSTRUCTOR:通过构造函数自动注入
    • AUTOWIRE_AUTODETECT:自动检测注入方式
  • dependencyCheck:依赖检查
    • DEPENDENCY_CHECK_NONE:不检查
    • DEPENDENCY_CHECK_OBJECTS:检查对象引用
    • DEPENDENCY_CHECK_SIMPLE:检查简单类型属性
  • autowireCandidate:是否是自动注入其他bean的候补,默认true。只影响根据类型的主动注入,不影响根据名称的自动注入。
  • primary:是否是自动注入主要候补
  • factoryBeanName:工厂bean名称
  • factoryMethodName:工厂bean方法名称
  • initMethodName:自定义初始化bean方法名称
  • destroyMethodName:自定义销毁bean方法名称

问题:

  1. 为什么在Service的BeanDefiniton打印信息中,没有关于userDao属性的相关信息呢?
  2. Bean与BeanDefinition的关系

在文章的最后统一给出解答。

3 第二阶段-Bean元信息解析阶段

解析过程就是把Bean元信息封装为BeanDefinition的过程。

因为API方式配置不同,相应的解析方式也不同。这里只讲解下XML文件和注解方式的解析,properties文件自行查阅相关文档。

  • 不同配置方式解析所需要的类
    • 注解:AnnotatedBeanDefinitionReader或者 ClassPathBeanDefinitionScanner
    • xml文件:XmlBeanDefinitionReader
    • properties文件:PropertiesBeanDefinitionReader
  • 说明:
    • 在new解析对象时,都需要一个BeanDefinitionRegister注册器类型的对象
    • 都是通过某个方法,在调用链中完成了解析和注册,并没有单独的解析注册接口(方法)。

3.1 注解方式解析

3.1.1 AnnotatedBeanDefinitionReader解析过程

以配置阶段注解配置示例为例,看下AnnotatedBeanDefinitionReader如何解析,示例代码如下:

    public void testAnnotateParse() {
        //定义一个spring容器,这个容器默认实现了BeanDefinitionRegistry,所以本身就是一个bean注册器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //定义一个注解方式的BeanDefinition读取器,需要传递一个BeanDefinitionRegistry(bean注册器)对象
        AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);
        // 解析注册
        annotatedBeanDefinitionReader.register(UserDao.class, UserService.class);


        //打印出注册的bean的配置信息
        for (String beanName : new String[]{"userDao", "userService"}) {
            //通过名称从容器中获取对应的BeanDefinition信息
            BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
            //获取BeanDefinition具体使用的是哪个类
            String beanDefinitionClassName = beanDefinition.getClass().getName();
            //通过名称获取bean对象
            Object bean = factory.getBean(beanName);
            //打印输出
            System.out.println(beanName + ":");
            System.out.println("    beanDefinitionClassName:" + beanDefinitionClassName);
            System.out.println("    beanDefinition:" + beanDefinition);
            System.out.println("    bean:" + bean);
        }

    }

也就是说解析注册通过AnnotatedBeanDefinitionReader的register()方法完成。下面我们通过源码来追踪下,经过了那些步骤(操作),解析出BeanDefinition。

  • regist()调用registerBean()

    	public void register(Class<?>... componentClasses) {
    		for (Class<?> componentClass : componentClasses) {
    			registerBean(componentClass);
    		}
    	}
    
  • registerBean()调用doRegisterBean()

    	public void registerBean(Class<?> beanClass) {
    		doRegisterBean(beanClass, null, null, null, null);
    	}
    
  • doRegisterBean()方法第一行:AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);通过AnnotatedGenericBeanDefinition构造方法new一个BeanDefinition对象。

    	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
    			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
    			@Nullable BeanDefinitionCustomizer[] customizers) {
    
    		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    		// ...省略其他代码
    		}
    

3.1.2 ClassPathBeanDefinitionScanner解析过程

下面看下ClassPathBeanDefinitionScanner解析实例代码:

ClassPathBeanDefinitionScanner beanDefinitionScanner = new ClassPathBeanDefinitionScanner(factory);
        beanDefinitionScanner.scan("com.gaogzhen.myspring.dao", "com.gaogzhen.myspring.service");

其他代码同上,这里调用scan()完成解析注册。下面通过源码跟踪下怎么完成解析的,我们只关注解析部分,其他暂时忽略。

  • 调用scan()方法里面doScan()方法

    	public int scan(String... basePackages) {
    		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    
    		doScan(basePackages);
    
    		// Register annotation config processors, if necessary.
    		if (this.includeAnnotationConfig) {
    			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    		}
    
    		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    	}
    
    
  • 276行doScan()执行findCandidateComponents()解析得到BeanDefinition集合

    	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) {
    			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    		//...省略其他代码
    		}
    
  • findCandidateComponents()判断如果有候选组件索引调用addCandidateComponentsFromIndex();否则调用scanCandidateComponents()。大部分情况下,没有开启候选组件索引生成,所以我们追踪下下面的方法。

    	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
                // 候选组件索引不为空且支持包含的过滤器
                // 通过生成的候选组件索引添加候选组件
    			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    		}
    		else {
                // 扫描包路径
    			return scanCandidateComponents(basePackage);
    		}
    	}
    
  • scanCandidateComponents()方法为解析生成BeanDefinition的核心方法

    	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    		Set<BeanDefinition> candidates = new LinkedHashSet<>();
    		try {
    			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);
    				}
    				try {
    					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
    					if (isCandidateComponent(metadataReader)) {
    						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;
    	}
    
    • 执行流程

      • 转换包路径为绝对路径

        • basePacke->packageSearchPath->绝对路径: 形式 “com.gaogzhen.myspring.dao”==>classpath*:com/gaogzhen/myspring/dao/**/*.class==>“K:/projects/java/framework/spring6/spring6-006-cyclelife/target/classes/com/gaogzhen/myspring/dao/UserDao.class”
      • 通过资源解析器包装路径下的class为Resource对象

      • 通过MetadataReaderFactory传递resource创建对应的MetadataReader

        • MetadataReader:访问类元数据信息
      • isCandidateComponent()判断是否是候选组件

        	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;
        	}
        
        • 判断如果和exclue过滤器内类型一致返回false;和include过滤器中类型一致返回true。

          • exclue过滤器默认为空;include过滤器中默认添加了Component.class,jakarta.annotation.ManagedBean,jakarta.inject.Named三种类型

            	protected void registerDefaultFilters() {
            		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
            		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
            		try {
            			this.includeFilters.add(new AnnotationTypeFilter(
            					((Class<? extends Annotation>) ClassUtils.forName("jakarta.annotation.ManagedBean", cl)), false));
            			logger.trace("JSR-250 'jakarta.annotation.ManagedBean' found and supported for component scanning");
            		}
            		catch (ClassNotFoundException ex) {
            			// JSR-250 1.1 API (as included in Jakarta EE) not available - simply skip.
            		}
            		try {
            			this.includeFilters.add(new AnnotationTypeFilter(
            					((Class<? extends Annotation>) ClassUtils.forName("jakarta.inject.Named", cl)), false));
            			logger.trace("JSR-330 'jakarta.inject.Named' annotation found and supported for component scanning");
            		}
            		catch (ClassNotFoundException ex) {
            			// JSR-330 API not available - simply skip.
            		}
            	}
            
        • 实际执行的match()方法为AbstractTypeHierarchyTraversingFilter中的

          	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
          			throws IOException {
          
          		// This method optimizes avoiding unnecessary creation of ClassReaders
          		// as well as visiting over those readers.
          		if (matchSelf(metadataReader)) {
          			return true;
          		}
          		ClassMetadata metadata = metadataReader.getClassMetadata();
          		if (matchClassName(metadata.getClassName())) {
          			return true;
          		}
          
          		if (this.considerInherited) {
          			String superClassName = metadata.getSuperClassName();
          			if (superClassName != null) {
          				// Optimization to avoid creating ClassReader for super class.
          				Boolean superClassMatch = matchSuperClass(superClassName);
          				if (superClassMatch != null) {
          					if (superClassMatch.booleanValue()) {
          						return true;
          					}
          				}
          				else {
          					// Need to read super class to determine a match...
          					try {
          						if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
          							return true;
          						}
          					}
          					catch (IOException ex) {
          						if (logger.isDebugEnabled()) {
          							logger.debug("Could not read super class [" + metadata.getSuperClassName() +
          									"] of type-filtered class [" + metadata.getClassName() + "]");
          						}
          					}
          				}
          			}
          		}
          
          		if (this.considerInterfaces) {
          			for (String ifc : metadata.getInterfaceNames()) {
          				// Optimization to avoid creating ClassReader for super class
          				Boolean interfaceMatch = matchInterface(ifc);
          				if (interfaceMatch != null) {
          					if (interfaceMatch.booleanValue()) {
          						return true;
          					}
          				}
          				else {
          					// Need to read interface to determine a match...
          					try {
          						if (match(ifc, metadataReaderFactory)) {
          							return true;
          						}
          					}
          					catch (IOException ex) {
          						if (logger.isDebugEnabled()) {
          							logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
          									metadata.getClassName() + "]");
          						}
          					}
          				}
          			}
          		}
          
          		return false;
          	}
          
        • 先判断注解类型是否匹配

          • 示例中匹配这里
        • 在判断类名是否匹配

        • 在判断是否是继承匹配

        • 在判断是否是接口匹配

      • ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);new新的ScannedGenericBeanDefinition类型的BeanDefinition。解析完成。

思考:

  • 候选组件索引是啥东西?

3.2 xml文件解析

通过XmlBeanDefinitionReader解析xml中配置的bean为对应的BeanDefinition,以配置中xml文件为例,下面给出解析实例代码:

    public void testXmlParse() {
        //定义一个spring容器,这个容器默认实现了BeanDefinitionRegistry,所以本身就是一个bean注册器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

        // XmlBeanDefinitionReader加载xml配置文件,解析生成BeanDefinition注册到容器中
        String location = "classpath:config.xml";
        int beanCount = reader.loadBeanDefinitions(location);
        System.out.println(String.format("共注册了 %s 个bean", beanCount));

        //打印出注册的bean的配置信息
        for (String beanName : factory.getBeanDefinitionNames()) {
            //通过名称从容器中获取对应的BeanDefinition信息
            BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
            //获取BeanDefinition具体使用的是哪个类
            String beanDefinitionClassName = beanDefinition.getClass().getName();
            //通过名称获取bean对象
            Object bean = factory.getBean(beanName);
            //打印输出
            System.out.println(beanName + ":");
            System.out.println("    beanDefinitionClassName:" + beanDefinitionClassName);
            System.out.println("    beanDefinition:" + beanDefinition);
            System.out.println("    bean:" + bean);
        }
    }

输出:

2023-03-08 09:43:06 521 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [config.xml]
共注册了 2 个bean
2023-03-08 09:43:06 526 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao'
userDao:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.gaogzhen.myspring.dao.UserDao]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [config.xml]
    bean:com.gaogzhen.myspring.dao.UserDao@55342f40
2023-03-08 09:43:06 536 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
userService:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.gaogzhen.myspring.service.UserService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [config.xml]
    bean:com.gaogzhen.myspring.service.UserService@306851c7

XmlBeanDefinitionReader调用loadBeanDefinitions()完成了xml配置文件的加载解析和注册,下面我们通过源码追踪下解析为BeanDefinition的流程。在追踪过程中其他非关键步骤,我们直接省略。

调用AbstractBeanDefinitionReader的loadBeanDefinitions()

	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}

继续调用loadBeanDefinitions()

	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		// 资源加载器
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int count = loadBeanDefinitions(resources);
			// 省略
	}

int count = loadBeanDefinitions(resources);继续调用loadBeanDefinitions()

	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int count = 0;
		for (Resource resource : resources) {
		   // 继续调用
			count += loadBeanDefinitions(resource);
		}
		return count;
	}

调用loadBeanDefinitions()回到XMLBeanDefinition的loadBeanDefinitions()方法

	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

分析:

  • EncodedResource :组合字符编码或者字符集,用于从resource读取信息

调用loadBeanDefinitions()方法

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		// 省略
        	// 继续调用
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	// 省略
	}

分析:

  • ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded 这个用于当前线程保存已加载resource
  • 在finally中使用完之后做了移除

继续调用doLoadBeanDefinitions()

	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
            // Document为解析xml文件后封装的对象
			Document doc = doLoadDocument(inputSource, resource);
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
	// 省略异常处理代码
	}

分析:

  • Document:HTML或者XML文档接口,表示文档树的根,提供访问数据的方法;文档中包含元素、文本结点、注释等等。

继续调用registerBeanDefinitions()

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

继续调用DefaultBeanDefinitionDocumentReader的registerBeanDefinitions()方法

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}

调用doRegisterBeanDefinitions()

	protected void doRegisterBeanDefinitions(Element root) {
		// 省略
		// preProcessXml为空方法,由子类去实现
		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
        // preProcessXml为空方法,由子类去实现
		postProcessXml(root);

		this.delegate = parent;
	}

继续调用parseBeanDefinitions()

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element ele) {
             // 判断结点是否是Element
            if (delegate.isDefaultNamespace(ele)) {
                // 判断是不是默认,即命名空间是不是"http://www.springframework.org/schema/beans"
               parseDefaultElement(ele, delegate);
            }
            else {
                // 自定义
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

分析:

  • 一般我们直接使用spring提供标准的xml beans配置

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    

继续调用parseDefaultElement()

	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            // <bean></bean>标签对应的元素
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// <beans></beans>标签对应的元素,会递归调用
			doRegisterBeanDefinitions(ele);
		}
	}

继续调用processBeanDefinition()

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		// 省略注册相关
	}

继续调用BeanDefinitionParserDelegate的parseBeanDefinitionElement()方法

	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
		return parseBeanDefinitionElement(ele, null);
	}

继续调用parseBeanDefinitionElement()

	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		// 省略
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	// 省略设置别名等操作
	}

分析:id这里获取<bean id=“xxx”>中的id,即beanName

继续调用parseBeanDefinitionElement()

	@Nullable
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, @Nullable BeanDefinition containingBean) {

		this.parseState.push(new BeanEntry(beanName));

		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}
		String parent = null;
		if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
			parent = ele.getAttribute(PARENT_ATTRIBUTE);
		}

		try {
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			parseConstructorArgElements(ele, bd);
			parsePropertyElements(ele, bd);
			parseQualifierElements(ele, bd);

			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));

			return bd;
		}
		// 省略异常处理
		return null;
	}

哎总算找到了封装BeanDefinition的地方。这里获取并设置BeanDefinition信息,比如className,parent,等等。

总结,解析封装BeanDefinition的过程:

  • 加载读取xml文件为Docment文档对象;
  • 递归获取Bean标签对应的Element元素;
  • 从Element元素获取BeanDefinition所需信息。

3.3 注解解析与xml解析比较

  • AnnotatedBeanDefinitionReader,传递Class对象,通过Class对象及其注解获取BeanDefinition信息,封装为AnnotatedGenericBeanDefinition对象。
  • ClassPathBeanDefinitionScanner需要扫描包下面.class文件,完成class加载,完成注解类型校验后获取BeanDefinition信息封装为ScannedGenericBeanDefinition对象。
  • XmlBeanDefinitionReader加载校验解析xml文件中bean,获取信息封装为GenericBeanDefinition对象。
  • XmlBeanDefinitionReader过程更为繁琐复杂;ClassPathBeanDefinitionScanner在没有开启候选组件索引生成的情况下不建议使用,原因在解答候选组件索引这个内容的时候给出;更为推荐AnnotatedBeanDefinitionReader解析Bean。

4 问题解答

  • 为什么在Service的BeanDefiniton打印信息中,没有关于userDao属性的相关信息呢?
  • Bean与BeanDefinition的关系

这两个问题在理解了Bean和BeanDefinition关系之后,就很容易理解,参考下面的[2]。

  • 候选组件索引

参考下面[3]

后记

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/spring6-study

参考:

[1]Spring系列之Bean生命周期详解[CP/OL].

[2]spring源码分析系列2:Bean与BeanDefinition关系[CP/OL].

[2]Spring 5 启动性能优化之 @Indexed[CP/OL].

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/395828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MapTask工作机制

目录 &#xff08;1&#xff09;Read阶段 &#xff08;2&#xff09;Map阶段 &#xff08;3&#xff09;Collect收集阶段 &#xff08;4&#xff09;Spill阶段 &#xff08;5&#xff09;Merge阶段 &#xff08;1&#xff09;Read阶段 MapTask通过InputFormat获得的Recor…

单协议 2.4GHz CC2651R31T0RGZR/CC2651R31T0RKPR无线MCU 802.15.4,蓝牙5.2

CC2651R31T0RGZR描述&#xff1a;具有 352KB 闪存的 SimpleLink 32 位 Arm Cortex-M4 单协议 2.4GHz 无线 MCU 48-VQFN -40C ~ 105C48QFN&#xff08;明佳达电子&#xff09;【介绍】CC2651R3器件是一款单协议 2.4 GHz 无线微控制器 (MCU)&#xff0c;支持以下协议&#xff1a;…

35- tensorboard的使用 (PyTorch系列) (深度学习)

知识要点 FashionMNIST数据集: 十种产品的分类. # T-shirt/top, Trouser, Pullover, Dress, Coat,Sandal, Shirt, Sneaker, Bag, Ankle Boot.writer SummaryWriter(run/fashion_mnist_experiment_1) # 网站显示一 tensorboard的使用 在网站显示pytorch的架构:1.1 …

常规非常规的卷及操作

最近看论文看到深度卷积的内容&#xff0c;然后就想着学习一下&#xff0c;发现论文中说的深度卷积并不是真正的深度卷积&#xff0c;感觉是分组卷积的一种&#xff0c;但是对于论文中得使用方式又有点不理解&#xff0c;就留下了一个问题放在了博客的最后&#xff0c;这里记录…

【蓝桥杯刷题】坑爹的负进制转换

【蓝桥杯刷题】——坑爹的负进制转换&#x1f60e;&#x1f60e;&#x1f60e; 目录 &#x1f4a1;前言&#x1f31e;&#xff1a; &#x1f49b;坑爹的负进制转换题目&#x1f49b; &#x1f4aa; 解题思路的分享&#x1f4aa; &#x1f60a;题目源码的分享&#x1f6…

Windows环境下Elasticsearch的下载与安装

一、elasticsearch下载地址1、官网&#xff1a;https://www.elastic.co/cn/即刻体验Elasticsearch2、直接解压3、进入bin目录双击.bat文件直接启动4、报错&#xff1a;error downloading geoip database [GeoLite2-City.mmdb]&#xff0c;elasticsearch.yml中增加&#xff1a;i…

FTP建议使用教程——以Xftp7为例

文件传输协议&#xff08;File Transfer Protocol&#xff0c;FTP&#xff09;是用于在网络上进行文件传输的一套标准协议&#xff0c;它工作在 OSI 模型的第七层&#xff0c; TCP 模型的第四层&#xff0c; 即应用层&#xff0c; 使用 TCP 传输而不是 UDP&#xff0c; 客户在和…

Jmeter+Ant+Jenkins接口自动化测试平台搭建

平台简介一个完整的接口自动化测试平台需要支持接口的自动执行&#xff0c;自动生成测试报告&#xff0c;以及持续集成。Jmeter支持接口的测试&#xff0c;Ant支持自动构建&#xff0c;而Jenkins支持持续集成&#xff0c;所以三者组合在一起可以构成一个功能完善的接口自动化测…

九.虚拟内存VM

1.寻址2.虚拟页VM将虚拟内存分割为称为虚拟页的大小固定的块1247是缓存的、36是未缓存的、05是未分配的下图为缺页及处理后页表将虚拟页映射到物理页页表是一个页表条目(PTE)的数组多个虚拟页面可以映射到同一个物理共享页面内存映射&#xff1a;将一组连续的虚拟页映射到一个文…

蓝牙耳机哪个品牌好一点?佩戴最舒服的蓝牙耳机排行

近年来&#xff0c;蓝牙耳机市场呈爆发式增长&#xff0c;越来越优秀的产品出现在大众视野。那么蓝牙耳机哪个品牌好一点&#xff1f;下面&#xff0c;我来给大家推荐几款佩戴最舒服的蓝牙耳机&#xff0c;一起来看看吧。 一、南卡小音舱蓝牙耳机 参考价&#xff1a;246 蓝牙…

一文带你彻底探究webgis坐标系知识(地理坐标系投影坐标系)

首先讲讲经纬度坐标系的起源与发展。其实目的很简单就是为了用数字化的方式来表示我们的地球和世界。 真正意义上与webgis相关的坐标系我们还得先来说说wgs84坐标系。这是老美1984年提出的一套坐标系。故命名为84坐标系。84坐标系是目前应用范围比较广的坐标系,但他并不是最早…

Elasticsearch:理解 Master,Elections,Quorum 及 脑裂

集群中的每个节点都可以分配多个角色&#xff1a;master、data、ingest、ml&#xff08;机器学习&#xff09;等。 我们在当前讨论中感兴趣的角色之一是 master 角色。 在 Elasticsearch 的配置中&#xff0c;我们可以配置一个节点为 master 节点。master 角色的分配表明该节点…

人机协同“全能秘书”丨 AI+金融“降本增效”全靠它!

近年来&#xff0c;我国金融科技蓬勃发展&#xff0c;以人工智能为主要代表的创新技术&#xff0c;为各大金融企业提供了更智能化的金融服务模式&#xff0c;科技与金融业务的深度融合&#xff0c;为金融行业注入新的发展动力。相比较传统人工客服&#xff0c;智能客服以算法为…

函数模板(template关键字的应用)

注释&#xff1a;本文主要介绍了函数模板的由来以及用法&#xff0c;还有关键字template。 我们感到时间的延续像一条我们无法逆行的小溪。 ——柏格森 文章目录一、语言的定式二、函数模板2.1 函数模板格式2.2 模板函数的实例化2.2.1隐式实例化/显式实例化2.3 模板参数的匹配…

SQL注入——floor报错注入

目录 一&#xff0c;涉及到的函数 rand&#xff08;&#xff09; floor&#xff08;&#xff09; concat_ws() as别名&#xff0c;group by分组 count() 报错原理 一&#xff0c;涉及到的函数 rand()函数&#xff1a;随机返回0~1间的小数 floor()函数&#xff1a;小数向…

C/C++开发,无可避免的多线程(篇六).线程池封装类

一、线程池概念 线程池是一种多线程处理方式&#xff0c;它包含一个线程工作队列和一个任务队列。当有任务需要处理时&#xff0c;线程池会从线程工作队列中取出一个空闲线程来处理任务&#xff0c;如果线程工作队列中没有空闲线程&#xff0c;则任务会被放入任务队列中等待处理…

M1、M2芯片Mac安装虚拟机

目录前言一、安装二、网络设置三、连接SSH客户端前言 一直想着给M1 Mac上安装虚拟机&#xff0c;奈何PD收费&#xff0c;找的破解也不稳定&#xff0c;安装上镜像就起不来。 注&#xff1a;挂长久的分享莫名其妙被封&#xff0c;需要安装包请私信我。 一、安装 虚拟机选择&a…

一次惨痛教训让我写了个Windows定期备份文件脚本

目录前言正文前言 说实话在写这篇文章的时候&#xff0c;咸鱼不禁又想起了那件男默女泪的往事 我喜欢做笔记&#xff0c;我觉得好记性不如烂笔头&#xff0c;所以在我的学习生涯以及职业生涯阶段&#xff0c;我用过四款笔记应用——Onenote、语雀、印象笔记、Typora 其中我个…

Nacos配置中心,分组配置参考,以及python、go、bash客户端连接获取

Nacos使用说明 nacos官方网站 https://nacos.io/zh-cn/docs/v2/what-is-nacos.html 1、基本配置说明 nacosIP地址&#xff1a;http://xxxxx:8848/nacos/ 服务管理端登录账号&#xff1a;nacos XXX Java最小配置&#xff0c;其他客户端可参考&#xff0c;配置可对应到第三章…

【动态规划】

动态规划1引言题目509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯小结53. 最大子数组和结语引言 蓝桥杯快开始了啊&#xff0c;自从报名后还没认真学过算法有(>﹏<)′&#xff0c;临时抱一下佛脚&#xff0c;一起学学算法。 题目 509. 斐波那契数 斐波那契数 &am…