java基础巩固-宇宙第一AiYWM:为了维持生计,Spring全家桶_Part1-2(学学Spring源码呗【两大神器:大管家与个性化产物】、【XmlBeanFactory争风吃醋】)~整起

PART1:构建环境的大体步骤

  • 首先呢,肯定是得把源码的运行环境准备好,咱们才能一步一步学呀。但是我自己呢,参考了很多文章和书,最后成功我觉得每个文章都有用,所以如果大家想看完整步骤,可以Google一下,我就记录一下自己构建环境的大体步骤:
    • 第一步:拉取Spring、安装Gradle、JDK、eclipse等。源码我接触到了两种方法拉源码:
      在这里插入图片描述
      • 第一种方式:直接去spring官网下载源码的zip
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
      • 第二种方式是,安装好Github或者Git,然后如果是Git Shell,其中执行git clone xxx,xxx就是下图中地址,网上有文章说,可以先把这个spring项目加到自己的Github或者Gitee码云仓库中,应该是能快一点,然后再把自己的地址放到xxx那里在Git Shell中运行,就行。
        在这里插入图片描述
        • 拉的时候可能会抱很多错,找找文章,去哪个配置文件中注释个什么东西…等等,具体问题具体分析
    • 第二步,执行gradlew.bat,下载各种依赖jar包等
      在这里插入图片描述
    • 第三步:gradlew.bat执行成功之后再执行gradlew eclipse命令生成 .classpath .project文件,为导入eclipse中做准备(也可以导入IDEA,我自己用的是eclipse)
      在这里插入图片描述
    • 第四步:导入到开发工具中,然后在Build Path以及其他地方按咱们平时做项目的经验修修补补,一天又一天。直到没错为止呗。
      在这里插入图片描述

PART2:【特此感谢郝老师团队的Spring源码解析这本书,没有这本书,也没有啥我的产出,特此感谢】然后,不管是SpringBoot项目还是SSM项目,咱们回想一下咱们的平时做项目的基石Spring,写一大堆配置文件:pom.xml、web.xml、然后Spring自己一个、SpringMVC一个、Mybatis一堆映射文件一个核心配置文件、然后用了其他的插件或者中间件时也都有自己配置文件,比如redis的、安全管理方面的、日志方面的…。前一篇咱们可以通过Spring的左膀开篇了解到一些概念。

  • 但是,归根结底,咱们的Spring要干几件事呢。
    • 1.读取配置文件:xxx.xml
      • 咋读配置文件,如果让咱们成天玩面向对象的人来干,不就是写个工具类xxxTemplate或者xxxUtils或者xxxYYY,去读取及验证配置文件。我们要用配置文件里面的东西,当然首先要做的就是读取,然后放置在内存中。
    • 2.根据配置文件中写好的配置找到对应的类的配置【咱们经常见得组件扫描,把一个包扫进去或者咱们写的 <bean id = ..., class = ...> 】,然后把这些类实例化放在IOC容器中,等别人通过名字byName或者byType去取,经常用的不就是@Autowired、@Qualified…
      • 这里面东西比较多,详细可以先看看前一篇,咱们这里只考虑,如果让我们自己来实现第二步,哦、我得先准备好几个类用于放代码的主要逻辑,比如我肯定是根据配置文件中的配置进行反射然后对类实例化,还有其他的逻辑。
  • Spring说,你起开,看我给你拿出两个神器:DefaultListableBeanFactory和XmlBeanDefinitionReader,办你上面那些事
    • DefaultListableBeanFactory:综合下面图上的所有功能,主要是对Bean注册后的处理。我愿称它为一个大管家:大管家看大门的,用到了再在eclipse中ctrl+o点点呗。挑几个继承结构里面重要的唠唠
      在这里插入图片描述
      在这里插入图片描述
      ...
      public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
      		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {...}
      ...
      
      • BeanFactory,生产 bean 的工厂,它负责生产和管理各个 bean 实例。ApplicationContext 其实就是一个 BeanFactory升级版或者叫增强版
    • XmlBeanDefinitionReader:这个神器是因为XmlBeanFactory在对DefaultListableBeanFactory类进行扩展时,XmlBeanFactory突然心一想,哎呀,我也不能啥都和父类DefaultListableBeanFactory类一样呀,我已经 注册及获取Bean都是使用从父类DefaultListableBeanFactory继承的方法去实现了,我要有个性,所以 XmlBeanFactory搞了一个与父类DefaultListableBeanFactory不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性,也就是在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。【所以总结一句话就是:XmlBeanFactory与DefaultListableBeanFactory不同的地方其实是在XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader .实现了个性化的BeanDefinitionReader读取。Spring配置文件封装成 Resource 后,读取工作就交给 XmlBeanDefinitionReader来处理。】
      在这里插入图片描述
      • XmlBeanDefinitionReader这个神器正在耀武扬威呢,人家XmlBeanFactory不干了,直接一个筋斗云翻过来再一个箭步冲到XmlBeanDefinitionReader面前说:哎,小子,你难道不知道你是因为我想要有个性你才出来的嘛,神气什么呢你神气,给XmlBeanFactory气的呀,一把鼻涕一把泪的。这XmlBeanDefinitionReader也傻了呀,我这也没说啥呀,咋给XmlBeanFactory激动成这样,好好好,你说你说,用你的言语和能力征服观众,
        • XmlBeanFactory:各位观众…
        • XmlBeanDefinitionReader:这脸变得也太快了吧。
    • XmlBeanFactory:各位观众,可别说你们不认识我,BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("xxxXxx.xml"));。确实,XmlBeanFactory把这一句抛出来,确实引起了很多用Spring框架的观众的共鸣。【很明显能看出,XmlBeanFactory 对DefaultListableBeanFactory进行了扩展,主要用于从 XML 配置文件中读取 BeanDefinition,对于注册和获取 Bean 都是使用父类 DefaultListableBeanFactory 继承的方法去实现。】不过人家观众也不傻,你不给人家细讲一下你这一句底层的实现逻辑能行?
      在这里插入图片描述
      • 代码中的 new ClassPathResource("xxxXXX.xml")这一句代表通过ClassPathResource对Spring的xxxXXX.xml配置文件读取过程进行封装,将配置文件封装为Resource类型的实例方法。其实这个让我想起来咱们的RPC,点点看这个东西,里面咱们的序列化协议和网络传输协议不是已经被封装好了嘛。另外,像项目的迭代过程中,咱们客户端去访问服务提供者暴露的服务时,自己兜里揣着入参去调用远程服务这个过程咱们不是也封装起来了嘛,最后才有了Stub、动态代理、Invoker这些东西嘛。跑偏了,回来。
      • 另外,除了上面那个还有,封装资源文件【当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类(EncodedResource用于对资源文件的编码进行处理的,你有文件你不处理一下文件的编码能行,看乱码?)进行封装】:除了配置文件的加载,咱们的资源文件的加载也是经常用到的。比如Resource resource=new ClassPathResource(“xxxXxx. xm1”);InputStream inputStream = resource.getInputStream();得到文件流之后就回到了javaSE了,可以愉快的玩耍了。得到了这个Resource接口我们便可以对所有资源文件进行统一处理。其实 ClassPathResource中的实现方式便是通过class或者classLoader提供的底层方法进行调用,而对于FileSystemResource的实现其实更简单,直接使用FileInputStream对文件进行实例化。当通过Resource相关类完成了对配置文件进行封装后配置文件的读取工作就全权交给XmlBeanDefinitionReader来处理了,XmlBeanDefinitionReader加载数据是通过XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)这个XmlBeanFactory的构造器中的this.reader.loadBeanDefinitions(resource)这一句实现资源加载的【而继续往下的话就知道,这么大的this.reader.,难道没啥想法吗。所以此时咱们就可以知道你XmlBeanFactory其实也是在自己的构造函数中调用的也是XmlBeanDefinitionReader类型的reader属性提供的方法this.reader.loadBeanDefinitions(resource),这个调用过程也可以算作是整个资源加载的切入点】。你俩也别超了,你因为个性化生成了我,我死心塌地的为你服务,相辅相成,一个棒棒的故事。
        在这里插入图片描述
        • 听到这当通过Resource相关类完成了对配置文件进行封装后配置文件的读取工作就全权交给XmlBeanDefinitionReader来处理了又开始得意洋洋的偷笑了,我是两大神器之一我牛B,给XmlBeanFactory气的呀,狠狠地瞪着XmlBeanDefinitionReader。但是说到这,咱们在看回程序,XmlBeanDefinitionReader加载数据前还有一个调用父类构造函数初始化的过程,XmlBeanDefinitionReader说凭啥呀,AiYWM编剧,为啥我玩数据之前就得来一个调用父类构造函数初始化的过程,AiYWM编剧说,因为呢,人家源码中在XmlBeanFactory的构造器中是这样写的:...super(parentBeanFactory);this.reader.loadBeanDefinitions(resource),还有疑问吗?,XmlBeanDefinitionReader说没了。那咱们不点一下这个super进去瞧瞧能行,走你。
      • 结果一下子冲到了AbstractAutowireCapableBeanFactory.java的中的AbstractAutowireCapableBeanFactory构造函数中的super()了…,目前这个还看不过啥逻辑,不过这个super说明不了啥,但是这个AbstractAutowireCapableBeanFactory构造函数中有很多 ignoreDependencyInterface方法,这个玩意其实就是用来偷懒的,比如 当A中有属性B,那么当Spring在获取A的Bean的时候如果其属性B还没有初始化,那么Spring会自动初始化B。但是,这个时候我要是不想去初始化B而直接获取A就回家吃饭呢【你想呀,快下班了,有人说,那个221号,把那个啥给咱改一下,把那个啥给咱们做一下…你再下班,你崩溃不?】,也就是B不会被初始化,此时就是ignoreDependencyInterface方法大显身手的时候了:
        public AbstractAutowireCapabl eBeanFactory() {
        	super() ;
        	//B实现了BeanNameAware接口B就不会被初始化,就直接可以获取A,人家Spring把这叫做自动装配时忽略给定的依赖接口,或者说通过其他方式解析Application上下文注册依赖【类似于BeanFactory通过BeanFactoryAware进行注入或者ApplicationContext通过ApplicationContextAware进行注入】
        	ignoreDependencyInterface(BeanNameAware.class) ;
        	ignoreDependencyInterface(BeanFactoryAware.class) ;
        	ignoreDependencyInterface(BeanClassLoaderAware.class) ;
        }
        
        • 说了半天还是没有真正开始玩数据处理数据呗,上面咱们半天在那干啥呢,其实人家这叫数据处理前的数据准备阶段,数据准备阶段的逻辑主要就是
          • 首先对传入的resource参数做封装【当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。构造了一个有编码(encoding)的InputStreamReader,当构造好encodedResource对象后,再次转入了可复用方法loadBeanDefinitions(new EncodedResource(resource))。】,目的是考虑到Resource可能存在编码要求的情况
            /**
            	 * Load bean definitions from the specified XML file.
            	 * @param resource the resource descriptor for the XML file
            	 * @return the number of bean definitions found
            	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
            	 */
            	@Override
            	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            	//EncodedResource这个类主要是用于对资源文件的编码进行处理的
            		return loadBeanDefinitions(new EncodedResource(resource));
            	}
            
          • 其次,从Resource中获取对应的InputStream并构造InputSource。【通过SAX读取XML文件的方式来准备InputSource对象。如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例。也就是说,对于解析一个XML,SAX首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。默认的寻找规则,即通过网络(实现上就是声明的DTD的URI地址)来下载相应的DTD声明,并进行认证。EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程,比如我们将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX即可。这样就避免了通过网络来寻找相应的声明。
            public int loadBeanDefinitions(EncodedResource encodedResource){
            ...
            	//从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputStream
            	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {...}
            }
            ...
            
          • 最后,通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions。【其实也就是将准备的数据通过参数传入真正的核心处理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource())。】
            public int loadBeanDefinitions(EncodedResource encodedResource){
            	...
            	return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            	...
            }
            
            • doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法可以总结或者说抽取为三件事:
              • doLoadBeanDefinitions方法中第一件事:获取对XML文件的验证模式 保证了XML文件的正确性【Spring通过getValidationModeForResource方法来获取对应资源的的验证模式。如果手动指定了验证模式(如果手动指定了验证模式则使用指定的验证模式)则使用指定的验证模式如果未指定则使用自动检测】。比较常用的验证模式有两种:DTD和XSD。【Spring用来检测验证模式的办法就是判断是否包含DOCTYPE,如果包含就是DTD,否则就是XSD。】
                protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
                	try {
                		Document doc = doLoadDocument(inputSource, resource);
                		...
                	}
                }
                
                doLoadDocument(InputSource inputSource, Resource resource){
                	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
                }
                
                • DTD(Document Type Definition)即文档类型定义,说白了 就是通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。要使用DTD验证模式的时候需要在XML文件的头部声明,咱们之前项目中不是就在Spring中使用DTD声明过嘛:
                  在这里插入图片描述
                • XML Schema语言就是XSD(XML Schemas Definition)。XML Schema描述了XML文档的结构。其实就是用一个指定的XML Schema来验证某个XML文档,以 检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析。【 在使用XML Schema文档对XML实例文档进行检验,除了要声明名称空间外(xmlns=http://www.Springframework.org/schema/beans),还必须指定该名称空间所对应的XML Schema文档的存储位置。 通过schemaLocation属性来指定名称空间所对应的XML Schema文档的存储位置,它包含两个部分,一部分是名称空间的URI,另一部分就是该名称空间所标识的XML Schema文件位置或URL地址】
                  在这里插入图片描述
              • doLoadBeanDefinitions方法中第二件事:加载XML文件,并得到对应的Document。经过了验证模式准备的步骤就可以进行Document加载了。XmlBeanFactoryReader类对于文档读取委托给了DocumentLoader接口去执行,那接口是一种规范肯定不起作用,所以 真正调用的是DefaultDocumentLoader
                • 首先创建DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象
                protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
                	try {
                		Document doc = doLoadDocument(inputSource, resource);
                		...
                	}
                }
                
                doLoadDocument(InputSource inputSource, Resource resource){
                	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
                }
                
              • doLoadBeanDefinitions方法中第三件事:把文件转换为Document后,根据返回的Document提取并注册Bean信息
                protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
                	try {
                		Document doc = doLoadDocument(inputSource, resource);
                		int count = registerBeanDefinitions(doc, resource);
                		...
                	}
                }
                
                doLoadDocument(InputSource inputSource, Resource resource){
                	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
                }
                
                • 当程序已经拥有或者说获得或者说得到返回的XML文档文件的Document实例对象时,会 进入XmlBeanDefinitionReader.java的registerBeanDefinitions(Document doc, Resource resource),在这个方法中调用了documentReader.registerBeanDefinitions(doc, createReaderContext (resource))来进行bean的注册与加载。
                  //参数doc是通过loadDocument加载转换出来的,在这个方法中很好地应用了面向对象中单一职责的原则,将逻辑处理委托给单一的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReader接口,实例化的工作是在createBeanDefinitionDocumentReader()中完成的
                  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStore
                  Exception {
                  	//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader。通过createBeanDefinitionDocumentReader方法,BeanDefinitionDocumentReader真正的类型其实已经是DefaultBeanDefinitionDocumentReader了。
                  	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader() ;
                  	//将环境变量设置其中
                  	documentReader.setEnvironment(this.getEnvironment());
                  	//在实例化BeanDefinitionReader时候会将BeanDefinitionRegistry传入,默认使用继承自Defaul tListableBeanFactory的子类
                  	//记录统计前BeanDefinition的加载个数
                  	int countBefore = getRegistry().getBeanDefinitionCount() ;
                  	//加载及注册bean
                  	documentReader.registerBeanDefinitions(doc, createReaderContext(resource)) ;
                  	//记录本次加载的BeanDefinition个数
                  	return getRegistry().getBeanDefinitionCount() - countBefore;
                  }
                  
                • 顺着上面的documentReader.registerBeanDefinitions(doc, createReaderContext(resource))这一句代码往进走发现,DefaultBeanDefinitionDocumentReader中有个registerBeanDefinitions方法【虽然咱们上面程序中写的是BeanDefinitionDocumentReader documentReader,但是程序中不是已经说了嘛,此时 通过createBeanDefinitionDocumentReader方法,BeanDefinitionDocumentReader真正的类型其实已经是DefaultBeanDefinitionDocumentReader了。】,这个registerBeanDefinition方法的重要目的之一就是提取root【Element root = doc.getDocumentElement() ;】,以便于再次将root作为参数继续BeanDefinition的注册。而到了doRegisterBeanDefinitions后才算是真正地开始进行解析了
                  在这里插入图片描述
                  在这里插入图片描述
                • doRegisterBeanDefinitions方法中也就是 首先是对profile的处理,【String profileSpec = root.getAttribute (PROFILE_ ATTRIBUTE】,然后开始进行解析,也就是对PROFILE_ATTRIBUTE属性的解析。其实 首先程序会获取beans节点是否定义了profile属性,如果定义了则会需要到环境变量中去寻找,所以这里首先断言environment不可能为空,因为profile是可以同时指定多个的,需要程序对其拆分,并解析每个profile是都符合环境变量中所定义的,不定义则不会浪费性能去解析。
                  在这里插入图片描述
                  protected void doRegisterBeanDefinitions(Element root) {
                  		BeanDefinitionParserDelegate parent = this.delegate;
                  		this.delegate = createDelegate(getReaderContext(), root, parent);
                  
                  		if (this.delegate.isDefaultNamespace(root)) {
                  			//处理profile属性
                  			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                  			if (StringUtils.hasText(profileSpec)) {
                  				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                  				// We cannot use Profiles.of(...) since profile expressions are not supported
                  				// in XML config. See SPR-12458 for details.
                  				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                  					if (logger.isDebugEnabled()) {
                  						logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource());
                  					}
                  					return;
                  				}
                  			}
                  		}
                  
                  		//解析前处理,留给子类实现
                  		preProcessXml(root);
                  		parseBeanDefinitions(root, this.delegate);
                  		//解析后处理,留给子类实现
                  		postProcessXml(root);
                  		this.delegate = parent;
                  	}
                  
                • 除了解析这个,doRegisterBeanDefinitions方法中就像上面的igonreXxx一样,有个重要分支给蹿出来了。preProcessXml(root)和postProcessXml(root)方法。这俩点进去是空的,为什么是空的呢,是因为preProcessXml(root)和postProcessXml(root)方法是模版方法模式,如果继承自DefaultBeanDefinitionDocumentReader的子类需要在Bean解析前后做一些处理的话,那么只需要重写这两个方法就可以了。哎,忽然想起来咱们上一篇文章中也提到了Bean什么什么前后做一些处理要实现或者集成Spring中哪个接口然后重写什么什么方法…,其实都一样,所以这里也体现出来人家Spring除了生态好,喜欢和大家一块玩,愿意接受集成外,人家也支持对自身进行一定的修改或者增强性改动等等
                  在这里插入图片描述
                  在这里插入图片描述
                  • 面向对象设计方法学中常说的一句话,一个类要么是面向继承的设计的,要么就用final修饰。在DefaultBeanDefinitionDocumentReader中并没有用final修饰,所以它是面向继承而设计的
                • doRegisterBeanDefinitions方法中除了对profile的处理、以及上面那俩preProcessXml(root)和postProcessXml(root)方法实现在Bean解析前后做一些处理之外,你看代码嘛,还有一句parseBeanDefinitions(root, this.delegate) ;这个parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法是在DefaultBeanDefinitionDocumentReader中的,在这个方法中就一句话:对beans进行处理,对beans进行处理,还是对beans进行处理。处理的时候那肯定有不同的处理方式或者其他区别呀,不然费力气搞这方法干啥。在Spring的XML配置里面有两大类Bean声明
                  public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
                  ...
                  	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
                  			//对beans的处理
                  			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) {
                  						//对bean的处理。对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement方法对自定义命名空间进行解析
                  						if (delegate.isDefaultNamespace(ele)) {
                  							parseDefaultElement(ele, delegate);
                  						}
                  						else {
                  							//对bean的处理
                  							delegate.parseCustomElement(ele);
                  						}
                  					}
                  				}
                  			}
                  			else {
                  				delegate.parseCustomElement(root);
                  			}
                  		}
                  	}
                  ...
                  }
                  
                  • Spring的XML配置里面的大类Bean声明之一:一个是默认的标签<bean id= " demo1", class = "demo.DemoByAiYWM">。不管怎样,你得对这两种不同方式的XML配置的标签进行读取以及解析吧,Spring是这样处理的,对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement方法对自定义命名空间进行解析**。而**判断是否默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI()获取命名空间,并与Spring中固定的命名空间http://www.Springframework.org/schema/beans进行比对。如果一致则认为是默认,否则就认为是自定义
                  • Spring的XML配置里面的大类Bean声明之一二:另一类就是自定义的标签<tx:annotation-driven>。如果是自定义的,那么就需要用户实现一些接口及配置了

到此位置了,已经引入到了咱们默认标签和咱们自定义的标签,说白了就是配置文件里面的东西,咱们光知道咋配咋配,但是配完了底层是怎样发挥作用的,不看能成?,走你…进入Part1-3

巨人的肩膀:
https://blog.csdn.net/qq_36434219/article/details/103950752(最近看的一篇,还有很多很多好的文章,特此感谢各位前辈)
Spring源码深度解析

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值