返回顶部
首页 > 资讯 > 精选 >SpringMVC初始化流程实例分析
  • 947
分享到

SpringMVC初始化流程实例分析

2023-07-02 15:07:09 947人浏览 独家记忆
摘要

本文小编为大家详细介绍“springMVC初始化流程实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“springmvc初始化流程实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.httpservl

本文小编为大家详细介绍“springMVC初始化流程实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“springmvc初始化流程实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

1.httpservletBean

HttpServletBean 继承自 HttpServlet,它负责将 init-param 中的参数注入到当前 Servlet 实例的属性中,同时也为子类提供了增加 requiredProperties 的能力,需要注意的是 HttpServletBean 并不依赖于 Spring 容器

大家知道,HttpServlet 的初始化是从 init 方法开始的,所以我们就先从 HttpServletBean 的 init 方法开始看起:

@Overridepublic final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) {  try {   BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);   ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());   bw.reGISterCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));   initBeanWrapper(bw);   bw.setPropertyValues(pvs, true);  }  catch (BeansException ex) {   if (logger.isErrorEnabled()) {    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);   }   throw ex;  } } // Let subclasses do whatever initialization they like. initServletBean();}

在这个方法里,首先获取到 Servlet 的所有配置并转为 PropertyValues,然后通过 BeanWrapper 修改目标 Servlet 的相关属性。BeanWrapper 是 Spring 中提供一个工具,使用它可以修改一个对象的属性,像下面这样:

public class Main {    public static void main(String[] args) {        User user = new User();        BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(user);        beanWrapper.setPropertyValue("username", "itboyhub");        PropertyValue pv = new PropertyValue("address", "www.itboyhub.com");        beanWrapper.setPropertyValue(pv);        System.out.println("user = " + user);    }}

最终输出:

user = User{username='itboyhub', address='www.itboyhub.com'}

所以前面的 bw 实际上就代表当前 DispatcherServlet 对象。

通过 BeanWrapper 修改目标 Servlet 的相关属性时,有一个 initBeanWrapper 方法是空方法,开发者如有需要可以在子类中实现该方法,并且完成一些初始化操作。

属性配置完成后,最终调用 initServletBean 方法进行 Servlet 初始化,然而该方法也是一个空方法,在子类中实现。

这就是 HttpServletBean 所做的事情,比较简单,加载 Servlet 相关属性并设置给当前 Servlet 对象,然后调用 initServletBean 方法继续完成 Servlet 的初始化操作。

2.FrameworkServlet

从前面的介绍可知,FrameworkServlet 初始化的入口方法就是 initServletBean,因此我们就从 FrameworkServlet#initServletBean 方法开始看起:

@Overrideprotected final void initServletBean() throws ServletException { //省略... try {  this.WEBApplicationContext = initWebApplicationContext();  initFrameworkServlet(); } catch (ServletException | RuntimeException ex) {  //省略... }}

这个方法原本挺长的,但是抛开日志打印异常抛出,剩下的核心代码其实就两行:

  • initWebApplicationContext 方法用来初始化 WebApplicationContext。

  • initFrameworkServlet 方法用来初始化 FrameworkServlet,但是这个方法是一个空方法,没有具体的实现。本来子类可以重写该方法做一些初始化操作,但是实际上子类并没有重写该方法,所以这个方法我们就暂且忽略之,不去分析了。

那么这里最为重要的其实就是 initWebApplicationContext 方法了,我们一起来看下:

protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext =   WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) {  wac = this.webApplicationContext;  if (wac instanceof ConfigurableWebApplicationContext) {   ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;   if (!cwac.isActive()) {    if (cwac.getParent() == null) {     cwac.setParent(rootContext);    }    configureAndRefreshWebApplicationContext(cwac);   }  } } if (wac == null) {  wac = findWebApplicationContext(); } if (wac == null) {  wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) {  synchronized (this.onRefreshMonitor) {   onRefresh(wac);  } } if (this.publishContext) {  String attrName = getServletContextAttributeName();  getServletContext().setAttribute(attrName, wac); } return wac;}

这里的逻辑也比较清晰:

  1. 首先获取 rootContext。在默认情况下,Spring 会将容器设置为 ServletContext 的一个属性,属性的 key 为 org.springframework.web.context.WebApplicationContext.ROOT,所以根据这个 key 就可以调用 ServletContext#getAttribute 方法获取到 rootContext 了。

  2. 获取 WebApplicationContext 实例,也就是给 wac 变量赋值的过程,这里存在三种可能性:1.如果已经通过构造方法给 webApplicationContext 赋值了,则直接将其赋给 wac 变量,同时,如果需要设置 parent 就设置,需要刷新就刷新。这种方式适用于 Servlet3.0 以后的环境,因为从 Servlet3.0 开始,才支持直接调用 ServletContext.addServlet 方法去注册 Servlet,手动注册的时候就可以使用自己提前准备好的 WebApplicationContext 了,这块松哥在我录制的 Spring Boot 视频中也讲过,感兴趣的小伙伴可以在公众号后台回复 vhr 查看视频详情;2.如果第一步没能成功给 wac 赋值,那么调用 findWebApplicationContext 方法尝试去 ServletContext 中查找 WebApplicationContext 对象,找到了就赋值给 wac;3.如果第二步没能成功给 wac 赋值,那么调用 createWebApplicationContext 方法创建一个 WebApplicationContext 对象并赋值给 wac,一般来说都是通过这种方式创建的 WebApplicationContext。这三套组合拳下来,wac 肯定是有值了。

  3. 当 ContextRefreshedEvent 事件没有触发时,调用 onRefresh 方法完成容器刷新(由于第一种和第三种获取 WebApplicationContext 的方式最终都会调用 configureAndRefreshWebApplicationContext 方法,然后发布事件,再将 refreshEventReceived 变量标记为 true,所以实际上只有第二种方式获取 wac 实例的时候,这里才会刷新,具体可以看下文分析)。

  4. 最后将 wac 保存到到 ServletContext 中。保存的时候会根据 publishContext 变量的值来决定是否保存,publishContext 可以在 web.xml 中配置 Servlet 时通过 init-param 进行配置,保存的目的是为了方便获取。

上面的这些步骤中,通过 createWebApplicationContext 方法创建 WebApplicationContext 对象需要和大家细说下,因为一般情况下就是通过这种方式创建的 WebApplicationContext。我们来看一下相关的方法:

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  throw new ApplicationContextException(    "Fatal initialization error in servlet with name '" + getServletName() +    "': custom WebApplicationContext class [" + contextClass.getName() +    "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac =   (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); String configLocation = getContextConfigLocation(); if (configLocation != null) {  wac.setConfigLocation(configLocation); } configureAndRefreshWebApplicationContext(wac); return wac;}protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) {  // The application context id is still set to its original default value  // -> assign a more useful id based on available infORMation  if (this.contextId != null) {   wac.setId(this.contextId);  }  else {   // Generate default id...   wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +     ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());  } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) {  ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); wac.refresh();}

这里一共涉及到两个方法:

createWebApplicationContext

首先获取到创建类型,并检查创建类型,没问题的话调用 instantiateClass 方法完成创建工作,然后给创建好的 wac 对象配置各种属性,配置的 configLocation 就是我们在 web.xml 文件中配置的 SpringMVC 配置文件路径,默认的文件路径是 /WEB-INF/[servletName]-servlet.xml

configureAndRefreshWebApplicationContext

configureAndRefreshWebApplicationContext 方法主要也是配置&刷新 WebApplicationContext,在这个方法里会调用 addApplicationListener 为 wac 添加一个监听器,监听的是 ContextRefreshedEvent 事件,当收到该事件后,会调用 FrameworkServlet 的 onApplicationEvent 方法,并在该方法中调用 onRefresh 方法完成刷新,刷新之后,会将 refreshEventReceived 变量标记为 true。

public void onApplicationEvent(ContextRefreshedEvent event) { this.refreshEventReceived = true; synchronized (this.onRefreshMonitor) {  onRefresh(event.getApplicationContext()); }}

这就是 FrameworkServlet#initServletBean 方法的大致工作逻辑。这里涉及到了 onRefresh 方法,但是这是一个空方法,在子类 DispatcherServlet 中实现了,所以接下来我们就来看 DispatcherServlet。

3.DispatcherServlet

这里我们就不废话了,直接来看 onRefresh 方法,如下:

@Overrideprotected void onRefresh(ApplicationContext context) { initStrategies(context);}protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context);}

在 onRefresh 方法中调用了 initStrategies 进行初始化操作。initStrategies 的内容其实很简单,就是九个组件的初始化。九个的初始化流程比较类似,这里我们以常见的视图解析器的初始化方法 initViewResolvers 为例,来一起看看初始化流程:

private void initViewResolvers(ApplicationContext context) { this.viewResolvers = null; if (this.detectAllViewResolvers) {  // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.  Map<String, ViewResolver> matchingBeans =    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);  if (!matchingBeans.isEmpty()) {   this.viewResolvers = new ArrayList<>(matchingBeans.values());   // We keep ViewResolvers in sorted order.   AnnotationAwareOrderComparator.sort(this.viewResolvers);  } } else {  try {   ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);   this.viewResolvers = Collections.singletonList(vr);  }  catch (NoSuchBeanDefinitionException ex) {   // Ignore, we'll add a default ViewResolver later.  } } // Ensure we have at least one ViewResolver, by registering // a default ViewResolver if no other resolvers are found. if (this.viewResolvers == null) {  this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);  if (logger.isTraceEnabled()) {   logger.trace("No ViewResolvers declared for servlet '" + getServletName() +     "': using default strategies from DispatcherServlet.properties");  } }}

一开始的 viewResolvers 变量是一个集合,解析出来的视图解析器对象都将放入这个集合中。

首先判断 detectAllViewResolvers 变量是否为 true,如果为 true,则直接去查找 Spring 容器中的所有视图解析器,将查找结果赋值给 viewResolvers,然后进行排序。默认情况下 detectAllViewResolvers 变量的值为 true,如果有需要,可以在 web.xml 中进行配置,像下面这样:

<servlet>    <servlet-name>springmvc</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <init-param>        <param-name>contextConfigLocation</param-name>        <param-value>classpath:spring-servlet.xml</param-value>    </init-param>    <init-param>        <param-name>detectAllViewResolvers</param-name>        <param-value>false</param-value>    </init-param>    <load-on-startup>1</load-on-startup></servlet><servlet-mapping>    <servlet-name>springmvc</servlet-name>    <url-pattern>/</url-pattern></servlet-mapping>

如果 detectAllViewResolvers 的值为 false,那么接下来就会去 Spring 容器中查找一个名为 viewResolver 的视图解析器,此时查找到的就是一个单独的视图解析器。

一般来说,我们并不需要在 web.xml 中去配置 detectAllViewResolvers 的值,视图解析器有多少个就加载多少个。

举个简单例子,我们在 SpringMVC 的配置文件中可能像下面这样配置视图解析器:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">    <property name="prefix" value="/WEB-INF/jsp/"/>    <property name="suffix" value=".jsp"/></bean>

默认情况下,这个 bean 的 id 有没有都行,如果有,取什么值都可以,反正最终都是通过类型而不是 id 去查找的视图解析器。但是如果你在 web.xml 中将 detectAllViewResolvers 修改为 false,那么这个 bean 的 id 取值就比较重要了,就一定要是 viewResolver。

如果在 Spring 容器中通过这两种方式(通过类型查找或通过 id 查找)都没有找到 ViewResolver 实例,那么会调用 getDefaultStrategies 方法去获取一个默认的 ViewResolver 实例。默认实例的获取方式如下:

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { if (defaultStrategies == null) {  try {   // Load default strategy implementations from properties file.   // This is currently strictly internal and not meant to be customized   // by application developers.   ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);   defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  }  catch (IOException ex) {   throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());  } } String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) {  String[] classNames = StringUtils.commaDelimitedListToStringArray(value);  List<T> strategies = new ArrayList<>(classNames.length);  for (String className : classNames) {   try {    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());    Object strategy = createDefaultStrategy(context, clazz);    strategies.add((T) strategy);   }   catch (ClassNotFoundException ex) {    throw new BeanInitializationException(      "Could not find DispatcherServlet's default strategy class [" + className +      "] for interface [" + key + "]", ex);   }   catch (LinkageError err) {    throw new BeanInitializationException(      "Unresolvable class definition for DispatcherServlet's default strategy class [" +      className + "] for interface [" + key + "]", err);   }  }  return strategies; } else {  return Collections.emptyList(); }}

这段代码其实也比较简单,就是通过反射去获取默认的视图解析器。

首先给 defaultStrategies 赋值,defaultStrategies 的值实际上就是从 DispatcherServlet.properties 文件中加载到的,我们来看下这个文件内容:

SpringMVC初始化流程实例分析

可以看到,这里一共定义了 8 个默认的键值对,有的值是一个,有的值是多个。前面 initStrategies 方法中一共要初始化九个组件,这里默认只定义了 8 个,少了一个 MultipartResolver,这也好理解,并非所有的项目都有文件上传,而且即使有文件上传,用哪一个具体的 MultipartResolver 也不好确定,还是要开发者自己决定。

defaultStrategies 其实加载到的就是这 8 个键值对,其中视图解析器对应的是 org.springframework.web.servlet.view.InternalResourceViewResolver,通过反射创建该类的实例,当 Spring 容器中不存在任何视图解析器的时候,默认的视图解析器即此。

这就是 initViewResolvers 的工作流程,另外 8 个也和它差不多,唯一不同的是 initMultipartResolver,如下:

private void initMultipartResolver(ApplicationContext context) { try {  this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); } catch (NoSuchBeanDefinitionException ex) {  this.multipartResolver = null; }}

可以看到,它只是根据 bean 的名字去查找 bean 实例,没有去查找默认的 MultipartResolver。

说到这里,松哥和大家多说一句 SpringMVC 配置中的小细节,

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">    <property name="prefix" value="/WEB-INF/jsp/"/>    <property name="suffix" value=".jsp"/></bean><bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"></bean>

上面这个关于视图解析器和文件上传解析器的配置,不知道小伙伴们有没有注意过,视图解析器的 id 可有可无,而文件上传解析器的 id 必须是 multipartResolver,回顾我们上面的源码分析,你就知道为啥了!

读到这里,这篇“SpringMVC初始化流程实例分析”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

--结束END--

本文标题: SpringMVC初始化流程实例分析

本文链接: https://lsjlt.com/news/342938.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • SpringMVC初始化流程实例分析
    本文小编为大家详细介绍“SpringMVC初始化流程实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“SpringMVC初始化流程实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.HttpServl...
    99+
    2023-07-02
  • SpringMVC初始化流程是什么
    本篇内容主要讲解“SpringMVC初始化流程是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringMVC初始化流程是什么”吧!框架源码是我们 Cod...
    99+
    2024-04-02
  • 深入了解SpringMVC初始化流程
    目录前言1.HttpServletBean2.FrameworkServlet3.DispatcherServlet4.小结前言 框架源码是我们 Coding 晋级中的必修课,SSM...
    99+
    2024-04-02
  • Vue实例初始化的示例分析
    这篇文章将为大家详细讲解有关Vue实例初始化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。以下正式开始:Vue官网的生命周期图示表重点说一下 new Vue()...
    99+
    2024-04-02
  • C#中变量初始化实例分析
    本文小编为大家详细介绍“C#中变量初始化实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#中变量初始化实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。请看下面的实例,使用了各种类型的变量:name...
    99+
    2023-06-17
  • initoutputstream初始化输出流源码分析
    目录正文音频 OutputStream初始化init_output_stream() 函数流程图正文 init_output_stream() 是一个公...
    99+
    2022-11-13
    init output stream初始化输出流 init output stream
  • java初始化分析
    关于初始化的一点体会 [@more@]class Egg2 {static int i=5; int j=5; static//父类静态变量初始化块首先被执行,在main方法之前。 { System.out.println("superCl...
    99+
    2023-06-03
  • vue-router初始化的示例分析
    这篇文章主要为大家展示了“vue-router初始化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue-router初始化的示例分析”这篇文章吧。v...
    99+
    2024-04-02
  • Java类初始化执行流程解析
     测试代码:   package com.test.ClassLaoderTest; public class test1 { public static Strin...
    99+
    2024-04-02
  • CSS5初始化模板的示例分析
    这篇文章给大家分享的是有关CSS5初始化模板的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。 CSS5初始化模板席卷了GBK编码内容和UTF-8编码内容的两个编码模板。...
    99+
    2024-04-02
  • Spring框架初始化的示例分析
    这篇文章将为大家详细讲解有关Spring框架初始化的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、Spring能做什么?Spring的主要目的是使J2EE易用和促进好编程习惯。倒置控制容器 S...
    99+
    2023-05-30
    spring
  • C++的定义,声明和初始化实例分析
    这篇文章主要介绍了C++的定义,声明和初始化实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++的定义,声明和初始化实例分析文章都会有所收获,下面我们一起来看看吧。定义变量的定义用于为变量分配存储空间,...
    99+
    2023-06-29
  • Netty启动流程服务端channel初始化源码分析
    目录服务端channel初始化回顾上一小节initAndRegister()方法init(Channel)方法前文传送门 Netty分布式server启动流程 服务端cha...
    99+
    2024-04-02
  • Netty分布式客户端接入流程初始化源码分析
    目录前文概述:第一节:初始化NioSockectChannelConfig创建channel跟到其父类DefaultChannelConfig的构造方法中再回到AdaptiveRec...
    99+
    2024-04-02
  • webstorm+vue初始化项目的示例分析
    小编给大家分享一下webstorm+vue初始化项目的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!vue新项目准备:1、安装nodejs,官网下载傻瓜安装node -v 验证2、...
    99+
    2024-04-02
  • Java类和对象初始化过程的示例分析
    这篇文章主要介绍了Java类和对象初始化过程的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。  问题引入  近日我在调试一个枚举类型的解析器程序,该解析器是将数据库内...
    99+
    2023-06-03
  • SharedPreference初始化源码分析
    本篇内容介绍了“SharedPreference初始化源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!初始化sp 内部将数据放到 xm...
    99+
    2023-07-05
  • Java中实例初始化和静态初始化的过程详解
    目录一、实例初始化1.1 实例初始化过程1.2 实例初始化的顺序1.3 实例初始化的注意事项二、静态初始化2.1 静态初始化过程2.2 静态初始化的顺序2.3 静态初始化的注意事项三...
    99+
    2023-05-18
    Java实例初始化和静态初始化 Java实例初始化 Java静态初始化 Java初始化
  • 初始Spring的示例分析
    小编给大家分享一下初始Spring的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!为什么要使用SpringWhy SpringSpring makes p...
    99+
    2023-06-20
  • Netty分布式Server启动流程服务端初始化源码分析
    目录第一节:服务端初始化group方法初始化成员变量初始化客户端Handler第一节:服务端初始化 首先看下在我们用户代码中netty的使用最简单的一个demo: //创建boss和...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作