剑指Spring源码(二)
这是春节后的第一篇博客,我在构思这篇博客的时候,一度想放弃,想想要不要换个东西写,因为毕竟个人水平有限,spring源码实在博大精深,不是我这个菜的抠脚的菜鸡可以驾驭的,怕误人子弟,还有就是源码分析类的博客实在是太难写了,和一般的博客真心不同,可能写了很多,自己都不知道自己在写些什么,但是还是要坚持,从接触博客的那一天开始,就非常佩服那些大神,乐于分享,无私奉献,我也从那些博客中学到了不少东西,慢慢的从一个嫩嫩的小菜鸡变成了秃头大菜鸡,其中最佩服的就是那些源码分析类的博客,虽然看不懂,但是从博客中,我分明读出了大神们对技术的热爱,对技术的坚持,对技术的执著。坚持!
在上一篇spring源码解析中,我们分析了
//根据参数类型可以知道,其实可以传入多个annotatedclasses,但是这种情况出现的比较少 public annotationconfigapplicationcontext(class<?>... annotatedclasses) { //调用无参构造函数,会先调用父类genericapplicationcontext的构造函数 //父类的构造函数里面就是初始化defaultlistablebeanfactory,并且赋值给beanfactory //本类的构造函数里面,初始化了一个读取器:annotatedbeandefinitionreader read,一个扫描器classpathbeandefinitionscanner scanner //scanner的用处不是很大,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的 this(); //把传入的类进行注册,这里有两个情况, //传入传统的配置类 //传入bean(虽然一般没有人会这么做 //看到后面会知道spring把传统的带上@configuration的配置类称之为full配置类,不带@configuration的称之为lite配置类 //但是我们这里先把带上@configuration的配置类称之为传统配置类,不带的称之为普通bean register(annotatedclasses); //刷新 refresh(); }
中的前两行代码,回顾下,这两行代码,主要是把我们的配置类和内置的几个后置处理器放到了两个集合中:
//beandefinitionmap是map<string, beandefinition>, //这里就是把beanname作为key,beandefinition作为value,推到map里面 this.beandefinitionmap.put(beanname, beandefinition); //beandefinitionnames就是一个list<string>,这里就是把beanname放到list中去 this.beandefinitionnames.add(beanname);
今天,我们来分析下第三行代码,即:
//刷新 refresh();
这个方法做了很多事情,让我们点开这个方法:
public void refresh() throws beansexception, illegalstateexception { synchronized (this.startupshutdownmonitor) { // prepare this context for refreshing. //刷新预处理,和主流程关系不大,就是保存了容器的启动时间,启动标志等 preparerefresh(); //defaultlistablebeanfactory // tell the subclass to refresh the internal bean factory. //和主流程关系也不大,最终获得了defaultlistablebeanfactory, // defaultlistablebeanfactory实现了configurablelistablebeanfactory configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory(); // prepare the bean factory for use in this context. //还是一些准备工作,添加了两个后置处理器:applicationcontextawareprocessor,applicationlistenerdetector //还设置了 忽略自动装配 和 允许自动装配 的接口,如果不存在某个bean的时候,spring就自动注册singleton bean //还设置了bean表达式解析器 等 preparebeanfactory(beanfactory); try { // allows post-processing of the bean factory in context subclasses. //这是一个空方法 postprocessbeanfactory(beanfactory); // invoke factory processors registered as beans in the context. //执行自定义的beanfactoryprocessor和内置的beanfactoryprocessor invokebeanfactorypostprocessors(beanfactory); // register bean processors that intercept bean creation. // 注册beanpostprocessor registerbeanpostprocessors(beanfactory); // initialize message source for this context. initmessagesource(); // initialize event multicaster for this context. initapplicationeventmulticaster(); // initialize other special beans in specific context subclasses. // 空方法 onrefresh(); // check for listener beans and register them. registerlisteners(); // instantiate all remaining (non-lazy-init) singletons. finishbeanfactoryinitialization(beanfactory); // last step: publish corresponding event. finishrefresh(); } catch (beansexception ex) { if (logger.iswarnenabled()) { logger.warn("exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // destroy already created singletons to avoid dangling resources. destroybeans(); // reset 'active' flag. cancelrefresh(ex); // propagate exception to caller. throw ex; } finally { // reset common introspection caches in spring's core, since we // might not ever need metadata for singleton beans anymore... resetcommoncaches(); } } }
里面有很多小方法,我们今天的目标是分析前五个小方法:
preparerefresh
从命名来看,就知道这个方法主要做了一些刷新前的准备工作,和主流程关系不大,主要是保存了容器的启动时间,启动标志等。
configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory()
这个方法和主流程关系也不是很大,可以简单的认为,就是把beanfactory取出来而已。
preparebeanfactory
//还是一些准备工作,添加了两个后置处理器:applicationcontextawareprocessor,applicationlistenerdetector //还设置了 忽略自动装配 和 允许自动装配 的接口,如果不存在某个bean的时候,spring就自动注册singleton bean //还设置了bean表达式解析器 等 preparebeanfactory(beanfactory);
这代码相比前面两个就比较重要了,我们需要点进去好好看看,做了什么操作:
protected void preparebeanfactory(configurablelistablebeanfactory beanfactory) { // tell the internal bean factory to use the context's class loader etc. beanfactory.setbeanclassloader(getclassloader());//设置类加载器 //设置bean表达式解析器 beanfactory.setbeanexpressionresolver(new standardbeanexpressionresolver(beanfactory.getbeanclassloader())); //属性编辑器支持 beanfactory.addpropertyeditorregistrar(new resourceeditorregistrar(this, getenvironment())); // configure the bean factory with context callbacks. //添加一个后置处理器:applicationcontextawareprocessor,此后置处理处理器实现了beanpostprocessor接口 beanfactory.addbeanpostprocessor(new applicationcontextawareprocessor(this)); //以下接口,忽略自动装配 beanfactory.ignoredependencyinterface(environmentaware.class); beanfactory.ignoredependencyinterface(embeddedvalueresolveraware.class); beanfactory.ignoredependencyinterface(resourceloaderaware.class); beanfactory.ignoredependencyinterface(applicationeventpublisheraware.class); beanfactory.ignoredependencyinterface(messagesourceaware.class); beanfactory.ignoredependencyinterface(applicationcontextaware.class); // beanfactory interface not registered as resolvable type in a plain factory. // messagesource registered (and found for autowiring) as a bean. //以下接口,允许自动装配,第一个参数是自动装配的类型,,第二个字段是自动装配的值 beanfactory.registerresolvabledependency(beanfactory.class, beanfactory); beanfactory.registerresolvabledependency(resourceloader.class, this); beanfactory.registerresolvabledependency(applicationeventpublisher.class, this); beanfactory.registerresolvabledependency(applicationcontext.class, this); // register early post-processor for detecting inner beans as applicationlisteners. //添加一个后置处理器:applicationlistenerdetector,此后置处理器实现了beanpostprocessor接口 beanfactory.addbeanpostprocessor(new applicationlistenerdetector(this)); // detect a loadtimeweaver and prepare for weaving, if found. if (beanfactory.containsbean(load_time_weaver_bean_name)) { beanfactory.addbeanpostprocessor(new loadtimeweaverawareprocessor(beanfactory)); // set a temporary classloader for type matching. beanfactory.settempclassloader(new contexttypematchclassloader(beanfactory.getbeanclassloader())); } //如果没有注册过bean名称为xxx,spring就自己创建一个名称为xxx的singleton bean //register default environment beans. if (!beanfactory.containslocalbean(environment_bean_name)) { beanfactory.registersingleton(environment_bean_name, getenvironment()); } if (!beanfactory.containslocalbean(system_properties_bean_name)) { beanfactory.registersingleton(system_properties_bean_name, getenvironment().getsystemproperties()); } if (!beanfactory.containslocalbean(system_environment_bean_name)) { beanfactory.registersingleton(system_environment_bean_name, getenvironment().getsystemenvironment()); } }
主要做了如下的操作:
- 设置了一个类加载器
- 设置了bean表达式解析器
- 添加了属性编辑器的支持
- 添加了一个后置处理器:applicationcontextawareprocessor,此后置处理器实现了beanpostprocessor接口
- 设置了一些忽略自动装配的接口
- 设置了一些允许自动装配的接口,并且进行了赋值操作
- 在容器中还没有xx的bean的时候,帮我们注册beanname为xx的singleton bean
postprocessbeanfactory(beanfactory)
//这是一个空方法 postprocessbeanfactory(beanfactory);
这是一个空方法,可能以后spring会进行扩展把。
invokebeanfactorypostprocessors(beanfactory)
//执行自定义的beanfactoryprocessor和内置的beanfactoryprocessor invokebeanfactorypostprocessors(beanfactory);
重点代码终于来了,可以说 这句代码是目前为止最重要,也是内容最多的代码了,我们有必要好好分析下:
protected void invokebeanfactorypostprocessors(configurablelistablebeanfactory beanfactory) { //getbeanfactorypostprocessors真是坑,第一次看到这里的时候,愣住了,总觉得获得的永远都是空的集合,掉入坑里,久久无法自拔 //后来才知道spring允许我们手动添加beanfactorypostprocessor //即:annotationconfigapplicationcontext.addbeanfactorypostprocessor(xxx); postprocessorregistrationdelegate.invokebeanfactorypostprocessors(beanfactory, getbeanfactorypostprocessors()); // detect a loadtimeweaver and prepare for weaving, if found in the meantime // (e.g. through an @bean method registered by configurationclasspostprocessor) if (beanfactory.gettempclassloader() == null && beanfactory.containsbean(load_time_weaver_bean_name)) { beanfactory.addbeanpostprocessor(new loadtimeweaverawareprocessor(beanfactory)); beanfactory.settempclassloader(new contexttypematchclassloader(beanfactory.getbeanclassloader())); } }
让我们看看第一个小方法的第二个参数:
public list<beanfactorypostprocessor> getbeanfactorypostprocessors() { return this.beanfactorypostprocessors; }
这里获得的是beanfactorypostprocessor,当我看到这里的时候,愣住了,通过idea的查找引用功能,我发现这个集合永远都是空的,根本没有代码为这个集合添加数据,很久都没有想通,后来才知道我们在外部可以手动添加一个后置处理器,而不是交给spring去扫描,即:
annotationconfigapplicationcontext annotationconfigapplicationcontext = new annotationconfigapplicationcontext(appconfig.class); annotationconfigapplicationcontext.addbeanfactorypostprocessor(xxx);
只有这样,这个集合才不会为空,但是应该没有很少会有人这么做吧,当然也有可能是我孤陋寡闻。
让我们点开invokebeanfactorypostprocessors方法:
public static void invokebeanfactorypostprocessors( configurablelistablebeanfactory beanfactory, list<beanfactorypostprocessor> beanfactorypostprocessors) { // invoke beandefinitionregistrypostprocessors first, if any. set<string> processedbeans = new hashset<>(); //beanfactory是defaultlistablebeanfactory,是beandefinitionregistry的实现类,所以肯定满足if if (beanfactory instanceof beandefinitionregistry) { beandefinitionregistry registry = (beandefinitionregistry) beanfactory; //regularpostprocessors 用来存放beanfactorypostprocessor, list<beanfactorypostprocessor> regularpostprocessors = new arraylist<>(); //registryprocessors 用来存放beandefinitionregistrypostprocessor //beandefinitionregistrypostprocessor扩展了beanfactorypostprocessor list<beandefinitionregistrypostprocessor> registryprocessors = new arraylist<>(); // 循环传进来的beanfactorypostprocessors,正常情况下,beanfactorypostprocessors肯定没有数据 // 因为beanfactorypostprocessors是获得手动添加的,而不是spring扫描的 // 只有手动调用annotationconfigapplicationcontext.addbeanfactorypostprocessor(xxx)才会有数据 for (beanfactorypostprocessor postprocessor : beanfactorypostprocessors) { // 判断postprocessor是不是beandefinitionregistrypostprocessor,因为beandefinitionregistrypostprocessor // 扩展了beanfactorypostprocessor,所以这里先要判断是不是beandefinitionregistrypostprocessor // 是的话,直接执行postprocessbeandefinitionregistry方法,然后把对象装到registryprocessors里面去 if (postprocessor instanceof beandefinitionregistrypostprocessor) { beandefinitionregistrypostprocessor registryprocessor = (beandefinitionregistrypostprocessor) postprocessor; registryprocessor.postprocessbeandefinitionregistry(registry); registryprocessors.add(registryprocessor); } else {//不是的话,就装到regularpostprocessors regularpostprocessors.add(postprocessor); } } // do not initialize factorybeans here: we need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // separate between beandefinitionregistrypostprocessors that implement // priorityordered, ordered, and the rest. //一个临时变量,用来装载beandefinitionregistrypostprocessor //beandefinitionregistry继承了postprocessorbeanfactorypostprocessor list<beandefinitionregistrypostprocessor> currentregistryprocessors = new arraylist<>(); // first, invoke the beandefinitionregistrypostprocessors that implement priorityordered. // 获得实现beandefinitionregistrypostprocessor接口的类的beanname:org.springframework.context.annotation.internalconfigurationannotationprocessor // 并且装入数组postprocessornames,我理解一般情况下,只会找到一个 // 这里又有一个坑,为什么我自己创建了一个实现beandefinitionregistrypostprocessor接口的类,也打上了@component注解 // 配置类也加上了@component注解,但是这里却没有拿到 // 因为直到这一步,spring还没有去扫描,扫描是在configurationclasspostprocessor类中完成的,也就是下面的第一个 // invokebeandefinitionregistrypostprocessors方法 string[] postprocessornames = beanfactory.getbeannamesfortype(beandefinitionregistrypostprocessor.class, true, false); for (string ppname : postprocessornames) { if (beanfactory.istypematch(ppname, priorityordered.class)) { //获得configurationclasspostprocessor类,并且放到currentregistryprocessors //configurationclasspostprocessor是很重要的一个类,它实现了beandefinitionregistrypostprocessor接口 //beandefinitionregistrypostprocessor接口又实现了beanfactorypostprocessor接口 //configurationclasspostprocessor是极其重要的类 //里面执行了扫描bean,import,importresouce等各种操作 //用来处理配置类(有两种情况 一种是传统意义上的配置类,一种是普通的bean)的各种逻辑 currentregistryprocessors.add(beanfactory.getbean(ppname, beandefinitionregistrypostprocessor.class)); //把name放到processedbeans,后续会根据这个集合来判断处理器是否已经被执行过了 processedbeans.add(ppname); } } //处理排序 sortpostprocessors(currentregistryprocessors, beanfactory); //合并processors,为什么要合并,因为registryprocessors是装载beandefinitionregistrypostprocessor的 //一开始的时候,spring只会执行beandefinitionregistrypostprocessor独有的方法 //而不会执行beandefinitionregistrypostprocessor父类的方法,即beanfactoryprocessor的方法 //所以这里需要把处理器放入一个集合中,后续统一执行父类的方法 registryprocessors.addall(currentregistryprocessors); //可以理解为执行configurationclasspostprocessor的postprocessbeandefinitionregistry方法 //spring热插播的体现,像configurationclasspostprocessor就相当于一个组件,spring很多事情就是交给组件去管理 //如果不想用这个组件,直接把注册组件的那一步去掉就可以 invokebeandefinitionregistrypostprocessors(currentregistryprocessors, registry); //因为currentregistryprocessors是一个临时变量,所以需要清除 currentregistryprocessors.clear(); // next, invoke the beandefinitionregistrypostprocessors that implement ordered. // 再次根据beandefinitionregistrypostprocessor获得beanname,看这个beanname是否已经被执行过了,有没有实现ordered接口 // 如果没有被执行过,也实现了ordered接口的话,把对象推送到currentregistryprocessors,名称推送到processedbeans // 如果没有实现ordered接口的话,这里不把数据加到currentregistryprocessors,processedbeans中,后续再做处理 // 这里才可以获得我们定义的实现了beandefinitionregistrypostprocessor的bean postprocessornames = beanfactory.getbeannamesfortype(beandefinitionregistrypostprocessor.class, true, false); for (string ppname : postprocessornames) { if (!processedbeans.contains(ppname) && beanfactory.istypematch(ppname, ordered.class)) { currentregistryprocessors.add(beanfactory.getbean(ppname, beandefinitionregistrypostprocessor.class)); processedbeans.add(ppname); } } //处理排序 sortpostprocessors(currentregistryprocessors, beanfactory); //合并processors registryprocessors.addall(currentregistryprocessors); //执行我们自定义的beandefinitionregistrypostprocessor invokebeandefinitionregistrypostprocessors(currentregistryprocessors, registry); //清空临时变量 currentregistryprocessors.clear(); // finally, invoke all other beandefinitionregistrypostprocessors until no further ones appear. // 上面的代码是执行了实现了ordered接口的beandefinitionregistrypostprocessor, // 下面的代码就是执行没有实现ordered接口的beandefinitionregistrypostprocessor boolean reiterate = true; while (reiterate) { reiterate = false; postprocessornames = beanfactory.getbeannamesfortype(beandefinitionregistrypostprocessor.class, true, false); for (string ppname : postprocessornames) { if (!processedbeans.contains(ppname)) { currentregistryprocessors.add(beanfactory.getbean(ppname, beandefinitionregistrypostprocessor.class)); processedbeans.add(ppname); reiterate = true; } } sortpostprocessors(currentregistryprocessors, beanfactory); registryprocessors.addall(currentregistryprocessors); invokebeandefinitionregistrypostprocessors(currentregistryprocessors, registry); currentregistryprocessors.clear(); } // now, invoke the postprocessbeanfactory callback of all processors handled so far. //registryprocessors集合装载beandefinitionregistrypostprocessor //上面的代码是执行子类独有的方法,这里需要再把父类的方法也执行一次 invokebeanfactorypostprocessors(registryprocessors, beanfactory); //regularpostprocessors装载beanfactorypostprocessor,执行beanfactorypostprocessor的方法 //但是regularpostprocessors一般情况下,是不会有数据的,只有在外面手动添加beanfactorypostprocessor,才会有数据 invokebeanfactorypostprocessors(regularpostprocessors, beanfactory); } else { // invoke factory processors registered with the context instance. invokebeanfactorypostprocessors(beanfactorypostprocessors, beanfactory); } // do not initialize factorybeans here: we need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! //找到beanfactorypostprocessor实现类的beanname数组 string[] postprocessornames = beanfactory.getbeannamesfortype(beanfactorypostprocessor.class, true, false); // separate between beanfactorypostprocessors that implement priorityordered, // ordered, and the rest. list<beanfactorypostprocessor> priorityorderedpostprocessors = new arraylist<>(); list<string> orderedpostprocessornames = new arraylist<>(); list<string> nonorderedpostprocessornames = new arraylist<>(); //循环beanname数组 for (string ppname : postprocessornames) { //如果这个bean被执行过了,跳过 if (processedbeans.contains(ppname)) { // skip - already processed in first phase above } //如果实现了priorityordered接口,加入到priorityorderedpostprocessors else if (beanfactory.istypematch(ppname, priorityordered.class)) { priorityorderedpostprocessors.add(beanfactory.getbean(ppname, beanfactorypostprocessor.class)); } //如果实现了ordered接口,加入到orderedpostprocessornames else if (beanfactory.istypematch(ppname, ordered.class)) { orderedpostprocessornames.add(ppname); } //如果既没有实现priorityordered,也没有实现ordered。加入到nonorderedpostprocessornames else { nonorderedpostprocessornames.add(ppname); } } //排序处理priorityorderedpostprocessors,即实现了priorityordered接口的beanfactorypostprocessor // first, invoke the beanfactorypostprocessors that implement priorityordered. sortpostprocessors(priorityorderedpostprocessors, beanfactory); //执行priorityorderedpostprocessors invokebeanfactorypostprocessors(priorityorderedpostprocessors, beanfactory); //执行实现了ordered接口的beanfactorypostprocessor // next, invoke the beanfactorypostprocessors that implement ordered. list<beanfactorypostprocessor> orderedpostprocessors = new arraylist<>(); for (string postprocessorname : orderedpostprocessornames) { orderedpostprocessors.add(beanfactory.getbean(postprocessorname, beanfactorypostprocessor.class)); } sortpostprocessors(orderedpostprocessors, beanfactory); invokebeanfactorypostprocessors(orderedpostprocessors, beanfactory); // 执行既没有实现priorityordered接口,也没有实现ordered接口的beanfactorypostprocessor // finally, invoke all other beanfactorypostprocessors. list<beanfactorypostprocessor> nonorderedpostprocessors = new arraylist<>(); for (string postprocessorname : nonorderedpostprocessornames) { nonorderedpostprocessors.add(beanfactory.getbean(postprocessorname, beanfactorypostprocessor.class)); } invokebeanfactorypostprocessors(nonorderedpostprocessors, beanfactory); // clear cached merged bean definitions since the post-processors might have // modified the original metadata, e.g. replacing placeholders in values... beanfactory.clearmetadatacache(); }
首先判断beanfactory是不是beandefinitionregistry的实例,当然肯定是的,然后执行如下操作:
定义了一个set,装载beanname,后面会根据这个set,来判断后置处理器是否被执行过了。
定义了两个list,一个是regularpostprocessors,用来装载beanfactorypostprocessor,一个是registryprocessors用来装载beandefinitionregistrypostprocessor,其中beandefinitionregistrypostprocessor扩展了beanfactorypostprocessor。beandefinitionregistrypostprocessor有两个方法,一个是独有的postprocessbeandefinitionregistry方法,一个是父类的postprocessbeanfactory方法。
循环传进来的beanfactorypostprocessors,上面已经解释过了,一般情况下,这里永远都是空的,只有手动add beanfactorypostprocessor,这里才会有数据。我们假设beanfactorypostprocessors有数据,进入循环,判断postprocessor是不是beandefinitionregistrypostprocessor,因为beandefinitionregistrypostprocessor扩展了beanfactorypostprocessor,所以这里先要判断是不是beandefinitionregistrypostprocessor,是的话,执行postprocessbeandefinitionregistry方法,然后把对象装到registryprocessors里面去,不是的话,就装到regularpostprocessors。
定义了一个临时变量:currentregistryprocessors,用来装载beandefinitionregistrypostprocessor。
getbeannamesfortype,顾名思义,是根据类型查到beannames,这里有一点需要注意,就是去哪里找,点开这个方法的话,就知道是循环beandefinitionnames去找,这个方法以后也会经常看到。这里传了beandefinitionregistrypostprocessor.class,就是找到类型为beandefinitionregistrypostprocessor的后置处理器,并且赋值给postprocessornames。一般情况下,只会找到一个,就是org.springframework.context.annotation.internalconfigurationannotationprocessor,也就是configurationannotationprocessor。这个后置处理器在上一节中已经说明过了,十分重要。这里有一个问题,为什么我自己写了个类,实现了beandefinitionregistrypostprocessor接口,也打上了@component注解,但是这里没有获得,因为直到这一步,spring还没有完成扫描,扫描是在configurationclasspostprocessor类中完成的,也就是下面第一个invokebeandefinitionregistrypostprocessors方法。
循环postprocessornames,其实也就是org.springframework.context.annotation.internalconfigurationannotationprocessor,判断此后置处理器是否实现了priorityordered接口(configurationannotationprocessor也实现了priorityordered接口),
如果实现了,把它添加到currentregistryprocessors这个临时变量中,再放入processedbeans,代表这个后置处理已经被处理过了。当然现在还没有处理,但是马上就要处理了。。。进行排序,priorityordered是一个排序接口,如果实现了它,就说明此后置处理器是有顺序的,所以需要排序。当然目前这里只有一个后置处理器,就是configurationclasspostprocessor。
把currentregistryprocessors合并到registryprocessors,为什么需要合并?因为一开始spring只会执行beandefinitionregistrypostprocessor独有的方法,而不会执行beandefinitionregistrypostprocessor父类的方法,即beanfactoryprocessor接口中的方法,所以需要把这些后置处理器放入一个集合中,后续统一执行beanfactoryprocessor接口中的方法。当然目前这里只有一个后置处理器,就是configurationclasspostprocessor。
可以理解为执行currentregistryprocessors中的configurationclasspostprocessor中的postprocessbeandefinitionregistry方法,这就是spring设计思想的体现了,在这里体现的就是其中的热插拔,插件化开发的思想。spring中很多东西都是交给插件去处理的,这个后置处理器就相当于一个插件,如果不想用了,直接不添加就是了。这个方法特别重要,我们后面会详细说来。
清空currentregistryprocessors,因为currentregistryprocessors是一个临时变量,已经完成了目前的使命,所以需要清空,当然后面还会用到。
再次根据beandefinitionregistrypostprocessor获得beanname,然后进行循环,看这个后置处理器是否被执行过了,如果没有被执行过,也实现了ordered接口的话,把此后置处理器推送到currentregistryprocessors和processedbeans中。
这里就可以获得我们定义的,并且打上@component注解的后置处理器了,因为spring已经完成了扫描,但是这里需要注意的是,由于configurationclasspostprocessor在上面已经被执行过了,所以虽然可以通过getbeannamesfortype获得,但是并不会加入到currentregistryprocessors和processedbeans。处理排序。
合并processors,合并的理由和上面是一样的。
执行我们自定义的beandefinitionregistrypostprocessor。
清空临时变量。
在上面的方法中,仅仅是执行了实现了ordered接口的beandefinitionregistrypostprocessor,这里是执行没有实现ordered接口的beandefinitionregistrypostprocessor。
上面的代码是执行子类独有的方法,这里需要再把父类的方法也执行一次。
执行regularpostprocessors中的后置处理器的方法,需要注意的是,在一般情况下,regularpostprocessors是不会有数据的,只有在外面手动添加beanfactorypostprocessor,才会有数据。
查找实现了beanfactorypostprocessor的后置处理器,并且执行后置处理器中的方法。和上面的逻辑差不多,不再详细说明。
这就是这个方法中做的主要的事情了,可以说是比较复杂的。但是逻辑还是比较清晰的,在第9步的时候,我说有一个方法会详细说来,现在就让我们好好看看这个方法究竟做了什么吧。
public void processconfigbeandefinitions(beandefinitionregistry registry) { list<beandefinitionholder> configcandidates = new arraylist<>(); string[] candidatenames = registry.getbeandefinitionnames();//获得所有的beandefinition的name,放入candidatenames数组 //循环candidatenames数组 for (string beanname : candidatenames) { beandefinition beandef = registry.getbeandefinition(beanname);//根据beanname获得beandefinition // 内部有两个标记位来标记是否已经处理过了 // 这里会引发一连串知识盲点 // 当我们注册配置类的时候,可以不加configuration注解,直接使用component componentscan import importresource注解,称之为lite配置类 // 如果加了configuration注解,就称之为full配置类 // 如果我们注册了lite配置类,我们getbean这个配置类,会发现它就是原本的那个配置类 // 如果我们注册了full配置类,我们getbean这个配置类,会发现它已经不是原本那个配置类了,而是已经被cgilb代理的类了 // 写一个a类,其中有一个构造方法,打印出“你好” // 再写一个配置类,里面有两个bean注解的方法 // 其中一个方法new了a 类,并且返回a的对象,把此方法称之为geta // 第二个方法又调用了geta方法 // 如果配置类是lite配置类,会发现打印了两次“你好”,也就是说a类被new了两次 // 如果配置类是full配置类,会发现只打印了一次“你好”,也就是说a类只被new了一次,因为这个类被cgilb代理了,方法已经被改写 if (configurationclassutils.isfullconfigurationclass(beandef) || configurationclassutils.isliteconfigurationclass(beandef)) { if (logger.isdebugenabled()) { logger.debug("bean definition has already been processed as a configuration class: " + beandef); } } //判断是否为配置类(有两种情况 一种是传统意义上的配置类,一种是普通的bean), //在这个方法内部,会做判断,这个配置类是full配置类,还是lite配置类,并且做上标记 //满足条件,加入到configcandidates else if (configurationclassutils.checkconfigurationclasscandidate(beandef, this.metadatareaderfactory)) { configcandidates.add(new beandefinitionholder(beandef, beanname)); } } // 如果没有配置类,直接返回 // return immediately if no @configuration classes were found if (configcandidates.isempty()) { return; } // sort by previously determined @order value, if applicable //处理排序 configcandidates.sort((bd1, bd2) -> { int i1 = configurationclassutils.getorder(bd1.getbeandefinition()); int i2 = configurationclassutils.getorder(bd2.getbeandefinition()); return integer.compare(i1, i2); }); // detect any custom bean name generation strategy supplied through the enclosing application context singletonbeanregistry sbr = null; // defaultlistablebeanfactory最终会实现singletonbeanregistry接口,所以可以进入到这个if if (registry instanceof singletonbeanregistry) { sbr = (singletonbeanregistry) registry; if (!this.localbeannamegeneratorset) { //spring中可以修改默认的bean命名方式,这里就是看用户有没有自定义bean命名方式,虽然一般没有人会这么做 beannamegenerator generator = (beannamegenerator) sbr.getsingleton(configuration_bean_name_generator); if (generator != null) { this.componentscanbeannamegenerator = generator; this.importbeannamegenerator = generator; } } } if (this.environment == null) { this.environment = new standardenvironment(); } // parse each @configuration class configurationclassparser parser = new configurationclassparser( this.metadatareaderfactory, this.problemreporter, this.environment, this.resourceloader, this.componentscanbeannamegenerator, registry); set<beandefinitionholder> candidates = new linkedhashset<>(configcandidates); set<configurationclass> alreadyparsed = new hashset<>(configcandidates.size()); do { //解析配置类(传统意义上的配置类或者是普通bean,核心来了) parser.parse(candidates); parser.validate(); set<configurationclass> configclasses = new linkedhashset<>(parser.getconfigurationclasses()); configclasses.removeall(alreadyparsed); // read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new configurationclassbeandefinitionreader( registry, this.sourceextractor, this.resourceloader, this.environment, this.importbeannamegenerator, parser.getimportregistry()); } this.reader.loadbeandefinitions(configclasses);//直到这一步才把import的类,@bean @importrosource 转换成beandefinition alreadyparsed.addall(configclasses);//把configclasses加入到alreadyparsed,代表 candidates.clear(); //获得注册器里面beandefinition的数量 和 candidatenames进行比较 //如果大于的话,说明有新的beandefinition注册进来了 if (registry.getbeandefinitioncount() > candidatenames.length) { string[] newcandidatenames = registry.getbeandefinitionnames();//从注册器里面获得beandefinitionnames set<string> oldcandidatenames = new hashset<>(arrays.aslist(candidatenames));//candidatenames转换set set<string> alreadyparsedclasses = new hashset<>(); //循环alreadyparsed。把类名加入到alreadyparsedclasses for (configurationclass configurationclass : alreadyparsed) { alreadyparsedclasses.add(configurationclass.getmetadata().getclassname()); } for (string candidatename : newcandidatenames) { if (!oldcandidatenames.contains(candidatename)) { beandefinition bd = registry.getbeandefinition(candidatename); if (configurationclassutils.checkconfigurationclasscandidate(bd, this.metadatareaderfactory) && !alreadyparsedclasses.contains(bd.getbeanclassname())) { candidates.add(new beandefinitionholder(bd, candidatename)); } } } candidatenames = newcandidatenames; } } while (!candidates.isempty()); // register the importregistry as a bean in order to support importaware @configuration classes if (sbr != null && !sbr.containssingleton(import_registry_bean_name)) { sbr.registersingleton(import_registry_bean_name, parser.getimportregistry()); } if (this.metadatareaderfactory instanceof cachingmetadatareaderfactory) { // clear cache in externally provided metadatareaderfactory; this is a no-op // for a shared cache since it'll be cleared by the applicationcontext. ((cachingmetadatareaderfactory) this.metadatareaderfactory).clearcache(); } }
- 获得所有的beanname,放入candidatenames数组。
- 循环candidatenames数组,根据beanname获得beandefinition,判断此beandefinition是否已经被处理过了。
- 判断是否是配置类,如果是的话。加入到configcandidates数组,在判断的时候,还会标记配置类属于full配置类,还是lite配置类,这里会引发一连串的知识盲点:
3.1 当我们注册配置类的时候,可以不加@configuration注解,直接使用@component @componentscan @import @importresource等注解,spring把这种配置类称之为lite配置类, 如果加了@configuration注解,就称之为full配置类。
3.2 如果我们注册了lite配置类,我们getbean这个配置类,会发现它就是原本的那个配置类,如果我们注册了full配置类,我们getbean这个配置类,会发现它已经不是原本那个配置类了,而是已经被cgilb代理的类了。
3.3 写一个a类,其中有一个构造方法,打印出“你好”,再写一个配置类,里面有两个被@bean注解的方法,其中一个方法new了a类,并且返回a的对象,把此方法称之为geta,第二个方法又调用了geta方法,如果配置类是lite配置类,会发现打印了两次“你好”,也就是说a类被new了两次,如果配置类是full配置类,会发现只打印了一次“你好”,也就是说a类只被new了一次,因为这个类被cgilb代理了,方法已经被改写。
3.4 具体的可以看我的这篇博客:https://www.cnblogs.com/codebear/p/10304605.html,里面有详细的说明。 - 如果没有配置类直接返回。
- 处理排序。
- 解析配置类,可能是full配置类,也有可能是lite配置类,这个小方法是此方法的核心,稍后具体说明。
- 在第6步的时候,只是注册了部分bean,像 @import @bean等,是没有被注册的,这里统一对这些进行注册。
下面是解析配置类的过程:
public void parse(set<beandefinitionholder> configcandidates) { this.deferredimportselectors = new linkedlist<>(); //循环传进来的配置类 for (beandefinitionholder holder : configcandidates) { beandefinition bd = holder.getbeandefinition();//获得beandefinition try { //如果获得beandefinition是annotatedbeandefinition的实例 if (bd instanceof annotatedbeandefinition) { parse(((annotatedbeandefinition) bd).getmetadata(), holder.getbeanname()); } else if (bd instanceof abstractbeandefinition && ((abstractbeandefinition) bd).hasbeanclass()) { parse(((abstractbeandefinition) bd).getbeanclass(), holder.getbeanname()); } else { parse(bd.getbeanclassname(), holder.getbeanname()); } } catch (beandefinitionstoreexception ex) { throw ex; } catch (throwable ex) { throw new beandefinitionstoreexception( "failed to parse configuration class [" + bd.getbeanclassname() + "]", ex); } } //执行deferredimportselector processdeferredimportselectors(); }
因为可以有多个配置类,所以需要循环处理。我们的配置类的beandefinition是annotatedbeandefinition的实例,所以会进入第一个if:
protected final void parse(annotationmetadata metadata, string beanname) throws ioexception { processconfigurationclass(new configurationclass(metadata, beanname)); }
protected void processconfigurationclass(configurationclass configclass) throws ioexception { //判断是否需要跳过 if (this.conditionevaluator.shouldskip(configclass.getmetadata(), configurationphase.parse_configuration)) { return; } configurationclass existingclass = this.configurationclasses.get(configclass); if (existingclass != null) { if (configclass.isimported()) { if (existingclass.isimported()) { existingclass.mergeimportedby(configclass); } // otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // explicit bean definition found, probably replacing an import. // let's remove the old one and go with the new one. this.configurationclasses.remove(configclass); this.knownsuperclasses.values().removeif(configclass::equals); } } // recursively process the configuration class and its superclass hierarchy. sourceclass sourceclass = assourceclass(configclass); do { sourceclass = doprocessconfigurationclass(configclass, sourceclass); } while (sourceclass != null); this.configurationclasses.put(configclass, configclass); }
重点在于doprocessconfigurationclass方法,需要特别注意,最后一行代码,会把configclass放入一个map,会在上面第7步中用到。
protected final sourceclass doprocessconfigurationclass(configurationclass configclass, sourceclass sourceclass) throws ioexception { //递归处理内部类,一般不会写内部类 // recursively process any member (nested) classes first processmemberclasses(configclass, sourceclass); // process any @propertysource annotations //处理@propertysource注解,@propertysource注解用来加载properties文件 for (annotationattributes propertysource : annotationconfigutils.attributesforrepeatable( sourceclass.getmetadata(), propertysources.class, org.springframework.context.annotation.propertysource.class)) { if (this.environment instanceof configurableenvironment) { processpropertysource(propertysource); } else { logger.warn("ignoring @propertysource annotation on [" + sourceclass.getmetadata().getclassname() + "]. reason: environment must implement configurableenvironment"); } } // process any @componentscan annotations //获得componentscan注解具体的内容,componentscan注解除了最常用的basepackage之外,还有includefilters,excludefilters等 set<annotationattributes> componentscans = annotationconfigutils.attributesforrepeatable( sourceclass.getmetadata(), componentscans.class, componentscan.class); //如果没有打上componentscan,或者被@condition条件跳过,就不再进入这个if if (!componentscans.isempty() && !this.conditionevaluator.shouldskip(sourceclass.getmetadata(), configurationphase.register_bean)) { //循环处理componentscans for (annotationattributes componentscan : componentscans) { // the config class is annotated with @componentscan -> perform the scan immediately //componentscan就是@componentscan上的具体内容,sourceclass.getmetadata().getclassname()就是配置类的名称 set<beandefinitionholder> scannedbeandefinitions = this.componentscanparser.parse(componentscan, sourceclass.getmetadata().getclassname()); // check the set of scanned definitions for any further config classes and parse recursively if needed for (beandefinitionholder holder : scannedbeandefinitions) { beandefinition bdcand = holder.getbeandefinition().getoriginatingbeandefinition(); if (bdcand == null) { bdcand = holder.getbeandefinition(); } if (configurationclassutils.checkconfigurationclasscandidate(bdcand, this.metadatareaderfactory)) { //递归调用,因为可能组件类有被@bean标记的方法,或者组件类本身也有componentscan等注解 parse(bdcand.getbeanclassname(), holder.getbeanname()); } } } } // process any @import annotations //处理@import注解 //@import注解是spring中很重要的一个注解,springboot大量应用这个注解 //@import三种类,一种是import普通类,一种是import importselector,还有一种是import importbeandefinitionregistrar //getimports(sourceclass)是获得import的内容,返回的是一个set processimports(configclass, sourceclass, getimports(sourceclass), true); // process any @importresource annotations //处理@importresource注解 annotationattributes importresource = annotationconfigutils.attributesfor(sourceclass.getmetadata(), importresource.class); if (importresource != null) { string[] resources = importresource.getstringarray("locations"); class<? extends beandefinitionreader> readerclass = importresource.getclass("reader"); for (string resource : resources) { string resolvedresource = this.environment.resolverequiredplaceholders(resource); configclass.addimportedresource(resolvedresource, readerclass); } } //处理@bean的方法,可以看到获得了带有@bean的方法后,不是马上转换成beandefinition,而是先用一个set接收 // process individual @bean methods set<methodmetadata> beanmethods = retrievebeanmethodmetadata(sourceclass); for (methodmetadata methodmetadata : beanmethods) { configclass.addbeanmethod(new beanmethod(methodmetadata, configclass)); } // process default methods on interfaces processinterfaces(configclass, sourceclass); // process superclass, if any if (sourceclass.getmetadata().hassuperclass()) { string superclass = sourceclass.getmetadata().getsuperclassname(); if (superclass != null && !superclass.startswith("java") && !this.knownsuperclasses.containskey(superclass)) { this.knownsuperclasses.put(superclass, configclass); // superclass found, return its annotation metadata and recurse return sourceclass.getsuperclass(); } } // no superclass -> processing is complete return null; }
- 递归处理内部类,一般不会使用内部类。
- 处理@propertysource注解,@propertysource注解用来加载properties文件。
- 获得componentscan注解具体的内容,componentscan注解除了最常用的basepackage之外,还有includefilters,excludefilters等。
- 判断有没有被@componentscans标记,或者被@condition条件带过,如果满足条件的话,进入if,进行如下操作:
4.1 执行扫描操作,把扫描出来的放入set,这个方法稍后再详细说明。
4.2 循环set,判断是否是配置类,是的话,递归调用parse方法,因为被扫描出来的类,还是一个配置类,有@componentscans注解,或者其中有被@bean标记的方法 等等,所以需要再次被解析。 - 处理@import注解,@import是spring中很重要的一个注解,正是由于它的存在,让spring非常灵活,不管是spring内部,还是与spring整合的第三方技术,都大量的运用了@import注解,@import有三种情况,一种是import普通类,一种是import importselector,还有一种是import importbeandefinitionregistrar,getimports(sourceclass)是获得import的内容,返回的是一个set,这个方法稍后再详细说明。
- 处理@importresource注解。
- 处理@bean的方法,可以看到获得了带有@bean的方法后,不是马上转换成beandefinition,而是先用一个set接收。
我们先来看4.1中的那个方法:
public set<beandefinitionholder> parse(annotationattributes componentscan, final string declaringclass) { //扫描器,还记不记在new annotationconfigapplicationcontext的时候 //会调用annotationconfigapplicationcontext的构造方法 //构造方法里面有一句 this.scanner = new classpathbeandefinitionscanner(this); //当时说这个对象不重要,这里就是证明了。常规用法中,实际上执行扫描的只会是这里的scanner对象 classpathbeandefinitionscanner scanner = new classpathbeandefinitionscanner(this.registry, componentscan.getboolean("usedefaultfilters"), this.environment, this.resourceloader); //判断是否重写了默认的命名规则 class<? extends beannamegenerator> generatorclass = componentscan.getclass("namegenerator"); boolean useinheritedgenerator = (beannamegenerator.class == generatorclass); scanner.setbeannamegenerator(useinheritedgenerator ? this.beannamegenerator : beanutils.instantiateclass(generatorclass)); scopedproxymode scopedproxymode = componentscan.getenum("scopedproxy"); if (scopedproxymode != scopedproxymode.default) { scanner.setscopedproxymode(scopedproxymode); } else { class<? extends scopemetadataresolver> resolverclass = componentscan.getclass("scoperesolver"); scanner.setscopemetadataresolver(beanutils.instantiateclass(resolverclass)); } scanner.setresourcepattern(componentscan.getstring("resourcepattern")); //addincludefilter addexcludefilter,最终是往list<typefilter>里面填充数据 //typefilter是一个函数式接口,函数式接口在java8的时候大放异彩,只定义了一个虚方法的接口被称为函数式接口 //当调用scanner.addincludefilter scanner.addexcludefilter 仅仅把 定义的规则塞进去,并么有真正去执行匹配过程 //处理includefilters for (annotationattributes filter : componentscan.getannotationarray("includefilters")) { for (typefilter typefilter : typefiltersfor(filter)) { scanner.addincludefilter(typefilter); } } //处理excludefilters for (annotationattributes filter : componentscan.getannotationarray("excludefilters")) { for (typefilter typefilter : typefiltersfor(filter)) { scanner.addexcludefilter(typefilter); } } boolean lazyinit = componentscan.getboolean("lazyinit"); if (lazyinit) { scanner.getbeandefinitiondefaults().setlazyinit(true); } set<string> basepackages = new linkedhashset<>(); string[] basepackagesarray = componentscan.getstringarray("basepackages"); for (string pkg : basepackagesarray) { string[] tokenized = stringutils.tokenizetostringarray(this.environment.resolveplaceholders(pkg), configurableapplicationcontext.config_location_delimiters); collections.addall(basepackages, tokenized); } // 从下面的代码可以看出componentscans指定扫描目标,除了最常用的basepackages,还有两种方式 // 1.指定basepackageclasses,就是指定多个类,只要是与这几个类同级的,或者在这几个类下级的都可以被扫描到,这种方式其实是spring比较推荐的 // 因为指定basepackages没有ide的检查,容易出错,但是指定一个类,就有ide的检查了,不容易出错,经常会用一个空的类来作为basepackageclasses // 2.直接不指定,默认会把与配置类同级,或者在配置类下级的作为扫描目标 for (class<?> clazz : componentscan.getclassarray("basepackageclasses")) { basepackages.add(classutils.getpackagename(clazz)); } if (basepackages.isempty()) { basepackages.add(classutils.getpackagename(declaringclass)); } //把规则填充到排除规则:list<typefilter>,这里就把 注册类自身当作排除规则,真正执行匹配的时候,会把自身给排除 scanner.addexcludefilter(new abstracttypehierarchytraversingfilter(false, false) { @override protected boolean matchclassname(string classname) { return declaringclass.equals(classname); } }); //basepackages是一个linkedhashset<string>,这里就是把basepackages转为字符串数组的形式 return scanner.doscan(stringutils.tostringarray(basepackages)); }
- 定义了一个扫描器scanner,还记不记在new annotationconfigapplicationcontext的时候,会调用annotationconfigapplicationcontext的构造方法,构造方法里面有一句 this.scanner = new classpathbeandefinitionscanner(this);当时说这个对象不重要,这里就是证明了。常规用法中,实际上执行扫描的只会是这里的scanner对象。
- 处理includefilters,就是把规则添加到scanner。
- 处理excludefilters,就是把规则添加到scanner。
- 解析basepackages,获得需要扫描哪些包。
- 添加一个默认的排除规则:排除自身。
- 执行扫描,稍后详细说明。
这里需要做一个补充说明,添加规则的时候,只是把具体的规则放入规则类的集合中去,规则类是一个函数式接口,只定义了一个虚方法的接口被称为函数式接口,函数式接口在java8的时候大放异彩,这里只是把规则方塞进去,并没有真正执行匹配规则。
我们来看看到底是怎么执行扫描的:
protected set<beandefinitionholder> doscan(string... basepackages) { assert.notempty(basepackages, "at least one base package must be specified"); set<beandefinitionholder> beandefinitions = new linkedhashset<>(); //循环处理basepackages for (string basepackage : basepackages) { //根据包名找到符合条件的beandefinition集合 set<beandefinition> 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); //由findcandidatecomponents内部可知,这里的candidate是scannedgenericbeandefinition //而scannedgenericbeandefinition是abstractbeandefinition和annotatedbeandefinition的之类 //所以下面的两个if都会进入 if (candidate instanceof abstractbeandefinition) { //内部会设置默认值 postprocessbeandefinition((abstractbeandefinition) candidate, beanname); } if (candidate instanceof annotatedbeandefinition) { //如果是annotatedbeandefinition,还会再设置一次值 annotationconfigutils.processcommondefinitionannotations((annotatedbeandefinition) candidate); } 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; }
因为basepackages可能有多个,所以需要循环处理,最终会进行bean的注册。下面再来看看findcandidatecomponents方法:
public set<beandefinition> findcandidatecomponents(string basepackage) { //spring支持component索引技术,需要引入一个组件,因为大部分情况不会引入这个组件 //所以不会进入到这个if if (this.componentsindex != null && indexsupportsincludefilters()) { return addcandidatecomponentsfromindex(this.componentsindex, basepackage); } else { return scancandidatecomponents(basepackage); } }
spring支持component索引技术,需要引入一个组件,大部分项目没有引入这个组件,所以会进入scancandidatecomponents方法:
private set<beandefinition> scancandidatecomponents(string basepackage) { set<beandefinition> candidates = new linkedhashset<>(); try { //把 传进来的类似 命名空间形式的字符串转换成类似类文件地址的形式,然后在前面加上classpath*: //即:com.xx=>classpath*:com/xx/**/*.class string packagesearchpath = resourcepatternresolver.classpath_all_url_prefix + resolvebasepackage(basepackage) + '/' + this.resourcepattern; //根据packagesearchpath,获得符合要求的文件 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 元数据读取器,解析resource,也可以理解为描述资源的数据结构 metadatareader metadatareader = getmetadatareaderfactory().getmetadatareader(resource); //在iscandidatecomponent方法内部会真正执行匹配规则 //注册配置类自身会被排除,不会进入到这个if if (iscandidatecomponent(metadatareader)) { scannedgenericbeandefinition sbd = new scannedgenericbeandefinition(metadatareader); sbd.setresource(resource); sbd.setsource(resource); if (iscandidatecomponent(sbd)) { if (debugenabled) { logger.debug("identified candidate component class: " + resource); } candidates.add(sbd); } else { if (d
上一篇: less学习二---变量
下一篇: xhprof扩展安装与使用
推荐阅读
-
《剑指offer》面试题6 重建二叉树
-
C#版剑指Offer-001二维数组中的查找
-
剑指offer11:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。(进制转换,补码反码)
-
剑指offer第二天
-
Spring MVC源码(二) ----- DispatcherServlet 请求处理流程 面试必问
-
[算法练习-剑指offer]题18.二叉树的镜像(Java)
-
剑指Offer积累-JZ1-二维数组中的查找
-
剑指offer37.序列化和反序列化二叉树(详解)
-
力扣OJ 剑指 Offer 68 - II. 236. 二叉树的最近公共祖先
-
剑指Offer04:二维数组中的查找(Java)