SpringIOC图解流程+源码解析(一)
在之前的文章中,咱们已经聊过什么是IOC、DI,以及两者的区别,今天我们来聊聊Spring如何应用的IOC,以及对其源码的解析过程。
下面我们从spring源码的角度去解析这个流程图,看看spring是如何实现的每一个步骤的。
首先还是找入口,从spring应用的角度上去找切入点,我们在使用spring的时候,往往都会去配置一个application.xml文件(里面配置了一些我们的需要的bean),所以spring在启动IOC容器的时候肯定是要去加载这个配置文件的,以一个单元测试的例子来看:
@Test public void test1() { //通过xml的解析完成IOC的启动。 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); //从spring容器中直接获取bean对象。 DemoBean bean = applicationContext.getBean(DemoBean.class); System.out.println(bean); }
从上述例子来看,当我们新建ClassPathXmlApplicationContext对象的时候,spring其实就已经帮我们启动了IOC容器,所以在后续我们可以根据参数直接获取bean对象。所以顺着这个思路走下去,看一下ClassPathXmlApplicationContext这个类的构造方法:
public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //设置配置文件属性,把classpath:application.xml 存到内存中。 setConfigLocations(configLocations); if (refresh) { //IOC容器初始化核心方法 refresh(); } }
在ClassPathXmlApplicationContext中调用到了一个核心方法就是refresh()方法,其实这个方法就是IOC容器启动的核心方法,下面我们重点解析一下这个方法:
@Override public void refresh() throws BeansException, IllegalStateException { //同步代码块,使用的是对象锁。在close的时候也会加锁,初始化和关闭ioc容器进行线程安全控制。 synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. /** * refresh的预处理逻辑, * 设置日志和状态值 * 对环境变量信息的一些验证(非空验证) */ prepareRefresh(); // Tell the subclass to refresh the internal bean factory. /** * 获取BeanFactory,默认实现是DefaultListableBeanFactory * 加载BeanDefinition, 并注册到 BeanDefinitionRegistry * */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. /** * BeanFactory 的预准备工作,BeanFactory进行一些设置,例如context的类加载器 */ prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //对BeanFactory 的一些后置处理工作 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //实例化了BeanFactoryPostProcessors接口的bean,并调用接口方法 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //注册 BeanPostProcessors(bean的后置处理器),在创建bean的前后执行。 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //初始化MessageSource(国际化功能) initMessageSource(); // Initialize event multicaster for this context.、 //完成初始化事件派发器 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //子类重写方法,在容器刷新的时候可以自定义逻辑,例如 创建tomcat、jetty等容器操作。 onRefresh(); // Check for listener beans and register them. //注册 监听器。 就是实现了 ApplicationListener 的接口的 bean registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. /** * 初始化创建非懒加载方式的单例bean * 设置属性值 * 初始化方法调用(例如afterProperties、init-method方法) * 调用BeanPostProcessor(后置处理器)对实例bean进行后置处理 */ finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //发布事件。 finishRefresh(); }
这里只分析refresh方法中较为核心的几个方法,其他方法只叙述其功能(其实我们自己在后续开发或者学习过程中也会分有重点):
obtainFreshBeanFactory()方法
该方法主要是做了两件事,第一是实例化了BeanFactory,第二件是加载BeanDefinition,并注册到BeanDefinitionRegistry。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //刷新bean工厂:实例化工厂并加载Bean定义信息。 refreshBeanFactory(); //将加载完Bean定义信息的工厂对象返回。 return getBeanFactory(); } @Override protected final void refreshBeanFactory() throws BeansException { //判断是否存在beanFactory if (hasBeanFactory()) { //如果存在,将bean销毁 destroyBeans(); //关闭工厂 closeBeanFactory(); } try { //创建bean 工厂,默认实例化了DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //设置序列化id beanFactory.setSerializationId(getId()); /** 自定义bean工厂设置 1.设置 是否允许bean定义信息的覆盖(因为配置文件可能是个数组,会存在id一样的bean定义信息) 2.设置 是否允许循环依赖 */ customizeBeanFactory(beanFactory); //加载BeanDefinition,加载bean定义信息 loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
上述代码中可以发现实例化完了BeanFactory(其实就是DefaultListableBeanFactory),加载bean定义信息的方法在loadBeanDefinitions中,让我们接下来看做的第二件事(加载bean定义信息以及注册):
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. /** * 实例化了一个BeanDefinitionReader,Bean定义信息的读取器。 */ XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. /** * 配置读取器对象的一些环境变量、资源加载器和资源解析器。 */ beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. /** * 初始化读取器 */ initBeanDefinitionReader(beanDefinitionReader); /** * 将读取器传入加载bean定义信息。 */ loadBeanDefinitions(beanDefinitionReader); } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } /** * 这里的getConfigLocations() 对应的是在刚开始在ClassPathXmlApplicationContext 的构造方法中的setConfigLocations()。 * 有了读取器,有了资源,便开始加载。 */ String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
代码的粘贴有部分跳转的代码省略了,因为方法的重载缘故太多的重载方法之间的调用,只粘贴重点方法。这里都粘贴了具体是哪个类了,在跟代码的时候很快能够定位到。
紧接着是调用到了AbstractBeanDefinitionReader对象中的方法loadBeanDefinitions:
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { /** * 获取资源加载器,在AbstractXmlApplicationContext.loadBeanDefinitions方法中已经set过了,这里直接get * 其就是就是 AbstractXmlApplicationContext 对象。 */ 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 { /** * 通过资源加载器将classpath的application.xml加载成 Resource对象。 */ Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); /** * 将Resource对象传入加载Bean定义信息。 */ int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
上述代码中最终调用了loadBeanDefinitions,经过跟进是调用了XmlBeanDefinitionReader的loadBeanDefinitions 方法:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } /** * 这里使用ThreadLocal 来保证加载资源的时候线程安全,当前线程加载资源的时候只加载当前线程所拥有的资源集合。 */ Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { /** * 将资源读取为 流对象,将流对象传入 进行bean定义信息加载 */ InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //bean定义加载。 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { /** * 加载成 Document对象 */ Document doc = doLoadDocument(inputSource, resource); /** * 注册bean定义信息 * 这里截止到本方法,bean定义信息的解析已经完成了,紧接着就是bean定义信息的注册 */ int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } ... //省略catch块 } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); /** * 1.计算注册前的Bean定义信息个数 * 2.bean定义信息注册 * 3.bean定义信息注册完减去以前的就是 新注册的个数 */ int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
在这里调用了BeanDefinitionDocumentReader对象的registerBeanDefinitions注册bean定义的方法,这个BeanDefinitionDocumentReader类是个接口,其默认实现类是
DefaultBeanDefinitionDocumentReader :
protected void doRegisterBeanDefinitions(Element root) { /** * 创建Bean定义信息解析委托类对象 */ BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { 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; } 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) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { /** * 如果是默认命名空间内的元素,即xml头文件中定义的标签元素 */ parseDefaultElement(ele, delegate); } else { /** * 自定义标签元素 */ delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { /** * import 元素标签处理 */ importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { /** * alias 别名元素标签处理 */ processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { /** * bean标签元素解析过程 */ processBeanDefinition(ele, delegate); } //嵌套bean 处理 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } } protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { /** * 将bean定义信息封装成 BeanDefinitionHolder对象。 */ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //如果有自定义标签,则处理自定义标签。 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. /** * 注册bean定义信息 */ BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
BeanDefinitionReaderUtils:
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. /** * 拿到bean的name和bean定义信息对象传入,注册bean定义信息 */ String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. /** * 如果bean 有别名的话。将别名也中注册 */ String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
上面代码调用registry的registerBeanDefinition方法,registry是BeanDefinitionRegistry对象,在这里其实现类是DefaultListableBeanFactory:
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } /** * 从bean定义信息map中获取一下,看看能否获取到,其实就是判断是否已经加载过了这个beanName */ BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { //是否允许覆盖,在创建beanFactory的时候自定义配置中已经有配置了。 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //如果允许覆盖,则覆盖bean定义信息。 this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { /** * 弯弯绕绕,终于到了家了 * 将bean 的id 和 bean定义信息对象存到map中。 */ // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } else if (isConfigurationFrozen()) { clearByTypeCache(); } }
以上是对IOC容器启动过程中bean定义信息加载的梳理过程。
在读spring源码的过程中会发现,源码中会有很多的doXX方法,这类方法基本算是很底层的方法,或者说核心实现方法。
invokeBeanFactoryPostProcessors方法
该方法主要是实例化并调用BeanDefinitionRegistryPostProcessors接口的方法以及实例化了BeanFactoryPostProcessors接口的bean,并调用接口方法:
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); /** * 判断是否是BeanDefinitionRegistry 接口,这里的beanFactory是 DefaultListableBeanFactory, 是BeanDefinitionRegistry的实现类,所以这里为true */ if (beanFactory instanceof BeanDefinitionRegistry) { //强转为BeanDefinitionRegistry BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; //定义BeanFactoryPostProcessor集合,没有实现BeanDefinitionRegistryPostProcessor接口的类。 List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); /** * 定义BeanDefinitionRegistryPostProcessor 集合 * BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor的子类,主要是处理对bean定义信息的拦截。 * 这个集合 主要是 为了统一执行 postProcessBeanFactory方法的 也就是BeanFactoryPostProcessor接口的方法。 */ List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); /** * 处理传入的beanFactoryPostProcessors,这里一般传入的为空,只有在初始化 上下文对象并且添加Bean工厂处理器的时候,这里的beanFactoryPostProcessors 才会有值。 */ for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { 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. /** * 定义当前需要处理的 bean定义信息后置处理器 的集合。 */ List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. /** * 从beanFactory中获取 实现了BeanDefinitionRegistryPostProcessor接口的 类名 */ String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { /** * 判断这里 实现了BeanDefinitionRegistryPostProcessor的接口的 类是否实现了 PriorityOrdered接口, * 如果是,则实例化该类并且放入currentRegistryProcessors集合中 */ if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } /** * 排序, 主要是根据是否实现PriorityOrdered、Ordered接口来排序, 这里是PriorityOrdered接口,下面会有对Ordered接口的处理 */ sortPostProcessors(currentRegistryProcessors, beanFactory); /** * 添加到 registryProcessors,用来最后执行 postProcessBeanFactory的集合 */ registryProcessors.addAll(currentRegistryProcessors); /** * 执行 实现了BeanDefinitionRegistryPostProcessor接口的 postProcessBeanDefinitionRegistry方法。 */ invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); //清除缓存 currentRegistryProcessors.clear(); /** *下面这一段代码跟 上面的一段代码 流程是一样的, 无非是处理了针对实现 Ordered 的类的处理逻辑。 */ // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. 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); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. /** * 执行剩下的 实现了 BeanDefinitionRegistryPostProcessor 的类,也就是不属于上面说的类型的类。 */ boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { /** * 如果已经在执行队列中了就跳过,如果是还没有处理的 ,则进行实例化并且添加到队列processedBeans中。 */ 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(); } /** * 将剩下的bean对象 统一执行 postProcessBeanFactory方法,也就是 BeanFactoryPostProcessor接口方法。 * 上述主要是执行 BeanDefinitionRegistryPostProcessor接口的方法,BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor的子类,子类方法执行完了,到父类的方法了 */ // Now, invoke the postProcessBeanFactory callback of all processors handled so far. invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } /** * 上述代码中, 是对实现了 BeanDefinitionRegistryPostProcessor接口的后置处理器的实例化和调用,下面是对 实现了 BeanFactoryPostProcessor接口的处理器的实例化和调用。 * */ // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. /** * 定义集合 用于存放 实现了PriorityOrdered接口的BeanFactoryPostProcessor类对象。 */ List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); /** * 定义集合 用于存放 实现了Ordered 的BeanFactoryPostProcessor类对象的BeanName。 */ List<String> orderedPostProcessorNames = new ArrayList<>(); /** * 定义集合 用于存放普通的BeanFactoryPostProcessor类对象的BeanName。 */ List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { //上述已经处理过的,什么都不做。 if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } //如果是实现了PriorityOrdered接口的,实例化并将bean 添加到 集合中。 else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } //如果是实现了Ordered接口的,将BeanName添加到集合中。 else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { //普通的BeanFactoryPostProcessor 对象 类名。 nonOrderedPostProcessorNames.add(ppName); } } /** * 排序,并且执行 实现了PriorityOrdered 接口的 BeanFactoryPostProcessor对象的postProcessBeanFactory拦截方法。 */ // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // Next, invoke the BeanFactoryPostProcessors that implement Ordered. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(); for (String postProcessorName : orderedPostProcessorNames) { /** * 根据beanName实例化 实现了Ordered接口的对象。放入缓存队列中 */ orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } /** * 排序,并且执行 实现了Ordered 接口的 BeanFactoryPostProcessor对象的postProcessBeanFactory拦截方法。 */ sortPostProcessors(orderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); /** * 同样的逻辑,根据BeanName 去找bean,没有则实例化出来放到 缓存队列中。 * 最后是执行 最普通的 BeanFactoryPostProcessor对象的 postProcessBeanFactory拦截方法。 */ // 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(); }
以上的代码是对invokeBeanFactoryPostProcessors的详细解析,这个方法是有点长了些,但是其实实现的思路还是很清晰的,慢慢去理一下你就知道这个方法是在干啥了,只不过分了几种情况罢了。
registerBeanPostProcessors()方法
该方法主要是对Bean的后置处理器的实例化和注册流程。
public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { /** * 从BeanFactory中获取 实现了 BeanPostProcessor接口的BeanName。 */ String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); // Register BeanPostProcessorChecker that logs an info message when // a bean is created during BeanPostProcessor instantiation, i.e. when // a bean is not eligible for getting processed by all BeanPostProcessors. int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); // Separate between BeanPostProcessors that implement PriorityOrdered, // Ordered, and the rest. /** * 定义集合 ,用于存放 实现了 PriorityOrdered接口的 对象 */ List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); /** * 定义集合, 用于存放针对实现了 MergedBeanDefinitionPostProcessor接口的实现类对象 * 实现MergedBeanDefinitionPostProcessor接口的实现类 会在bean实例化之后,调用其接口的方法postProcessMergedBeanDefinition。 */ List<BeanPostProcessor> internalPostProcessors = new ArrayList<>(); /** * 定义集合,用于存放 实现了 Ordered接口类的 beanName */ List<String> orderedPostProcessorNames = new ArrayList<>(); /** * 定义集合, 用于存放 普通 的类的Beanname */ List<String> nonOrderedPostProcessorNames = new ArrayList<>(); for (String ppName : postProcessorNames) { /** * 遍历从BeanFactory中获取 bean,如果没有则实例化并返回 */ if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); priorityOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } } /** * 排序,针对实现了 PriorityOrdered、Ordered接口的 bean * 注册这些bean 到 BeanFactory中。 */ // First, register the BeanPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); /** * 下面是对 实现了 Ordered接口的 类对象进行注册,实现流程跟上面对PriorityOrdered实现类是一样的 */ // Next, register the BeanPostProcessors that implement Ordered. List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(); for (String ppName : orderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); orderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } sortPostProcessors(orderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, orderedPostProcessors); /** * 处理 普通bean的流程,将bean 注册到bean工厂中。 */ // Now, register all regular BeanPostProcessors. List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // Finally, re-register all internal BeanPostProcessors. sortPostProcessors(internalPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, internalPostProcessors); // Re-register post-processor for detecting inner beans as ApplicationListeners, // moving it to the end of the processor chain (for picking up proxies etc). beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); }
其实beanFactoryPostProcessor和BeanPostProcessor,这两个针对工厂和bean的后置处理器从源码来看其实大致逻辑基本一样,只不过操作的对象不一样罢了,一个是对工厂的增强,一个是对bean的增强。
finishBeanFactoryInitialization方法
该方法主要是初始化所以剩下的 非懒加载的 单例bean。
AbstractApplicationContext:
/** * 初始化所以剩下的 非懒加载的 单例bean: * 初始化创建非懒加载方式的单例bean * 设置属性值 * 初始化方法调用(例如afterProperties、init-method方法) * 调用BeanPostProcessor(后置处理器)对实例bean进行后置处理 */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. /** * 初始化 bean工厂的 转换服务,主要用于一些数据类型转换的。 */ if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. /** * 初始化 LoadTimeWeaverAware 对象实例 */ String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); /** * 实例化所有剩下的单例对象 */ // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
最后调用的是DefaultListableBeanFactory的preInstantiateSingletons方法:
@Override public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. /** * 定义集合,存储所有BeanName的集合的。 作为工厂内BeanName集合的副本 */ List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { /** * 合并bean定义信息,因为bean本身就是有父子类的关系,所以这里需要进行父子bean定义信息的合并。 */ RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); /** * 这里对bean定义的判断,是否是抽象类、是否是单例的、是否是懒加载 */ if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { /** * 判断是否是 工厂Bean,即 是否实现了FactoryBean 接口。 */ if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { /** * 开始初始化 */ getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
上面代码的getBean方法,调用的是AbstractBeanFactory的doGetBean方法,如下:
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { /** * 转换beanName 如果有别名存在,则将别名和id进行转换 */ String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. /** * 根据BeanName尝试从缓存中获取 bean对象 */ Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } //针对 FactoryBean的处理 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. //如果是prototype类型并且开启循环依赖,则抛出异常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. /** * 检查 父工厂中是否存在该对象。 */ BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } if (!typeCheckOnly) { //标记bean为已经创建 markBeanAsCreated(beanName); } try { //合并父子bean的 属性 RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. /** * 处理dependsOn,spring支持在创建一个bean之前,必须要先创建另一个bean,相当于一个bean要依赖另一个bean。 */ String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } /** * 实例化单例对象 */ // Create bean instance. if (mbd.isSingleton()) { //这里入参是个函数式接口,通过拉姆达表达式进行实现,也是一种延迟创建bean的机制,最终会调用到拉姆达表达式里的逻辑。 sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } ...//此处省略其他代码。 return (T) bean; }
上面doGetBean方法调用了DefaultSingletonBeanRegistry的getSingleton方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { /** * 从singletonObjects单例池中 尝试获取一下 */ Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //判断该bean是否正在被销毁 if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } /** * 验证完后续要真正开始创建对象了,这里需要把bean标志为正在创建, * 因为springbean的创建过程很复杂,步骤多,所以需要有状态标识。 */ beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { /** * singletonFactory 延迟创建bean * singletonFactory是个ObjectFactory对象,ObjectFactory是个函数式接口,所以这里调用getObject方法就是执行拉姆达表达式中的逻辑。 */ singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { /** * 这里是将 已经创建好的bean实例放到 单例池中,即一级缓存。 */ addSingleton(beanName, singletonObject); } } return singletonObject; } }
上面是getSingleton的实现逻辑,在该方法中可以看到最终是调用了singletonFactory.getObject(),而singletonFactory该对象是外面传过来的函数式接口对象,所以这里调用getObject方法就是在执行拉姆达表达式中的代码,即createBean方法,createBean调用了同类中的doCreateBean方法,即AbstractAutowireCapableBeanFactory的doCreateBean方法:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { /** * 实例化对象,这里是 通过反射的机制将对象实例化,仅仅是实例化,但并非设置属性。 * 来到这个步骤就是 springbean生命周期的开始了。 */ instanceWrapper = createBeanInstance(beanName, mbd, args); } /** * 获取bean实例对象。 */ Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. /** * 这里是对合并bean定义信息的后置处理器方法的执行,即实现了 MergedBeanDefinitionPostProcessor接口的类的对象。 */ synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. /** * 处理循环依赖,将刚刚实例化的bean 放到三级缓存中。 */ boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } /** * 这里是将这段函数式接口的拉姆达表达式放到了三级缓存中,因为getEarlyBeanReference中有实例化的bean参数,并且该方法的 * 返回值是该bean或者该bean的增强,所以这里也就可以理解为将bean放到了三级缓存中。 */ addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { /** * 填充属性 */ populateBean(beanName, mbd, instanceWrapper); /** * 调用初始化方法, 包括实现了InitializingBean接口、配置了init-Method的方法。 */ exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. /** * 注册销毁对象时候的 destory方法的bean对象(如果有的话)。 */ try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
以上是对单例bean的加载过程,在上面的doCreateBean方法中还有一些填充属性、初始化方法(包括Aware接口执行、后置处理器的执行等)的解析没有细致跟进去,以及涉及到bean循环依赖,后续我会在bean生命周期和bean循环依赖的篇幅中去详细介绍。
完整流程图:
原文链接:https://blog.csdn.net/qq_35009833/article/details/121495601
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)