当前位置: 首页 > news >正文

SpringBoot学习05-[SpringBoot的嵌入式Servlet容器]

SpringBoot的嵌入式Servlet容器

  • 嵌入式Servlet容器
    • servlet容器-嵌入式servlet容器配置修改
      • 通过全局配置文件修改修改
      • 添加实现了WebServerFactoryCustomizer接口的bean来进行修改
    • servlet容器-注册servlet三大组件
      • 应该如何注册呢?
        • servlet3.0规范提供的注解方式进行注册
        • springboot提供的注册方式
    • servlet容器-切换到其他servlet容器
    • servlet容器-嵌入式servlet容器自动配置原理
      • ServletWebServerFactoryAutoConfiguration配置类源码分析
        • 为什么可以根据配置依赖自动使用servlet容器?
        • 怎么根据配置文件中的server.xxx以及实现了WebServerFactoryCustomizer的类去设置serverlet容器配置?
        • 嵌入式servlet容器如何启动?
    • servlet容器-使用外部servlet容器
      • SpringBoot使用外部servlet
    • servlet容器-外部servlet启动SpringBoot原理
      • 启动原理

嵌入式Servlet容器

SpringBoot包含对嵌入式Tomcat、Jetty、Undertow等服务器的支持。大多数开发人员使用适当的“”启动器“” 来获取完全配置的实例。默认情况下,嵌入式服务器在port上监听HTTP请求8080

servlet容器-嵌入式servlet容器配置修改

在这里插入图片描述

通过全局配置文件修改修改

  • 可以通过server.xxx 来进行web服务配置,没有带服务器名称的则是通用配置
  • 通常带了具体服务器名称则是单独对该服务器进行设置,比如server.tomcat.xxx就是专门针对tomcat的配置

添加实现了WebServerFactoryCustomizer接口的bean来进行修改

这儿的泛型添加的tomcat的ConfigurableTomcatWebServerFactory

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> {@Overridepublic void customize(ConfigurableTomcatWebServerFactory server) {server.setPort(8088);}
}

在这里插入图片描述

servlet容器-注册servlet三大组件

  • servlet三大组件
  • servlet
  • 监听器:listenser
  • 过滤器:filter

应该如何注册呢?

  • servlet3.0规范提供的注解方式进行注册
  • springboot提供的注册方式
servlet3.0规范提供的注解方式进行注册

@WebServlet:注册servlet的注解
@WebListener:注册监听器的注解
@WebFilter:注册过滤器的注解

  • 以webservlet进行演示
  • 定义一个servlet
@WebServlet(name = "HelloServlet",urlPatterns = "/HelloServlet")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().println("hello servlet");}
}
  • 在springboot启动类上加上@ServletComponentScan,让springboot可以扫描到serverlet的bean
@SpringBootApplication
@ServletComponentScan
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class,args);}
}
springboot提供的注册方式
  • 自定义一个servlet
public class BeanServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().println("bean servlet");}
}
  • 定义一个bean注册的配置类
    可以使用ServletRegistrationBean、ServletListenerRegistrationBean、FilterRegistrationBean分别来管理servlet、监听器、过滤器
@Configuration
public class RegistryBeanConfigration {@Beanpublic ServletRegistrationBean myServlet(){ServletRegistrationBean<Servlet> registrationBean = new ServletRegistrationBean<>();//设置相应的servletregistrationBean.setServlet(new BeanServlet());//设置名称registrationBean.setName("beanServlet");//添加映射规则registrationBean.addUrlMappings("/beanServlet");return registrationBean;}
}
  • 测试
    在这里插入图片描述

servlet容器-切换到其他servlet容器

srpingboot默认服务器是tomcat服务器
在这里插入图片描述

  • 排除内嵌tomcat
        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!--排除内嵌tomcat--><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency>
  • 引入Jetty依赖
    <!--引入jetty依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>
  • 启动项目
    在这里插入图片描述
  • 测试
    在这里插入图片描述

servlet容器-嵌入式servlet容器自动配置原理

内嵌servelet自动配置类:ServletWebServerFactoryAutoConfiguration

  • 问题:
  • 为什么可以根据配置依赖自动使用servlet容器?
  • 怎么根据配置文件中的server.xxx以及实现了WebServerFactoryCustomizer的类去设置serverlet容器配置?
  • 嵌入式servlet容器如何启动?

ServletWebServerFactoryAutoConfiguration配置类源码分析

//启用配置文件的属性类,那么所有的配置信息都会绑定到ServerProperties.class
@EnableConfigurationProperties(ServerProperties.class)

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
//只要依赖了任何一个servlet容器,它就会生效
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//启用配置文件的属性类,那么所有的配置信息都会绑定到ServerProperties.class
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
为什么可以根据配置依赖自动使用servlet容器?
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  • 通过@imort导入Embeddel类
  • 每个Embeddel类 中,都配置了相应的@ConditionalOnClass,会根据当前servlet容器启动器依赖判断classpath是否存在对应的类,如果存在就使用对应的sevlet容器,比如tomcat:
@Configuration(proxyBeanMethods = false)//只要添加了tomcat的场景启动器 则该注解才会匹配,如果没有对应的tomcat场景启动器,该注解就不会匹配@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {
怎么根据配置文件中的server.xxx以及实现了WebServerFactoryCustomizer的类去设置serverlet容器配置?
  • ServletWebServerFactoryAutoConfiguration类
	@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,ObjectProvider<WebListenerRegistrar> webListenerRegistrars,ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));}

在这里插入图片描述

ServletWebServerFactoryCustomizer 也实现了WebServerFactoryCustomizer,说明它也是定制servlet容器的

  • ServletWebServerFactoryCustomizer类
    根据配置文件中server.xxx来进行定制servlet容器
	@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();map.from(this.serverProperties::getPort).to(factory::setPort);map.from(this.serverProperties::getAddress).to(factory::setAddress);map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);map.from(this.serverProperties::getSsl).to(factory::setSsl);map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);map.from(this.serverProperties::getCompression).to(factory::setCompression);map.from(this.serverProperties::getHttp2).to(factory::setHttp2);map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);for (WebListenerRegistrar registrar : this.webListenerRegistrars) {registrar.register(factory);}if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);}}
  • 实现了WebServerFactoryCustomizer接口的类如何进行配置呢?
	@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}

在这里插入图片描述
TomcatServletWebServerFactoryCustomizer 也实现了WebServerFactoryCustomizer,说明它也是定制servlet容器的

  • TomcatServletWebServerFactoryCustomizer也重写了customize方法
	@Overridepublic void customize(TomcatServletWebServerFactory factory) {ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());}if (tomcatProperties.getRedirectContextRoot() != null) {customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot());}customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects());factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled());}
  • 怎么让所有的WebServerFactoryCustomizer bean一一调用呢?
  • BeanPostProcessorsRegistrar
    在这里插入图片描述
  • 注册WebServerFactoryCustomizerBeanPostProcessor为bean
	@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",//注册了WebServerFactoryCustomizerBeanPostProcessor beanWebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}
  • WebServerFactoryCustomizerBeanPostProcessor
    实现了BeanPostProcessor,在spring初始化bean的时候会被调用
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 判断当前创建的bean是不是webserverfactofyif (bean instanceof WebServerFactory) {this.postProcessBeforeInitialization((WebServerFactory)bean);}return bean;}

当前对应的Embeddedxxx 启用时,就会在里面配置一个WebServerFactory类型的bean,负责创建对应的容器和启动
在这里插入图片描述

	@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}

spring bean初始化前就会调用postProcessBeforeInitialization方法从而执行customize方法

  • 调用getCustomizers()方法
  • 调用getWebServerFactoryCustomizerBeans()方法,获取所有实现了WebServerFactoryCustomizer接口的bean(获取自定义的、ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer)
  • 在invoke方法中循环调用所有实现了WebServerFactoryCustomizer接口 的bean,并调用customize()方法进行一一定制
   private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {((LambdaSafe.Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {customizer.customize(webServerFactory);});}private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {if (this.customizers == null) {this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);this.customizers = Collections.unmodifiableList(this.customizers);}return this.customizers;}private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();}
嵌入式servlet容器如何启动?
  • TomcatServletWebServerFactory
  • 自动配置根据不同的依赖,启动对应一个Enbenddedxxx,然后配置一个对应的servlet工厂类,比如TomcatServletWebServerFactory
  • 在springboot应用启动的时候,就会调用refresh方法,onRefresh方法,调用getWebServer,创建servlet容器并且启动

TomcatServletWebServerFactory类

    public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());Iterator var4 = this.serverLifecycleListeners.iterator();while(var4.hasNext()) {LifecycleListener listener = (LifecycleListener)var4.next();tomcat.getServer().addLifecycleListener(listener);}Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);this.customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);this.configureEngine(tomcat.getEngine());Iterator var8 = this.additionalTomcatConnectors.iterator();while(var8.hasNext()) {Connector additionalConnector = (Connector)var8.next();tomcat.getService().addConnector(additionalConnector);}this.prepareContext(tomcat.getHost(), initializers);//启动方法在getTomcatWebServerreturn this.getTomcatWebServer(tomcat);}
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());}
  • TomcatWebServer类
    在TomcatWebServer中调用initialize方法进行启动
    private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));synchronized(this.monitor) {try {this.addInstanceIdToEngineName();Context context = this.findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource()) && "start".equals(event.getType())) {this.removeServiceConnectors();}});this.tomcat.start();this.rethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());} catch (NamingException var5) {}this.startDaemonAwaitThread();} catch (Exception var6) {this.stopSilently();this.destroySilently();throw new WebServerException("Unable to start embedded Tomcat", var6);}}}

servlet容器-使用外部servlet容器

  • 外部servlet容器
    • 服务器 安装tomcat 配置环境变量
    • 部署: war包—>运维—>tomcat webapp startup.sh 启动
    • 开发:将开发绑定本地tomcat
  • 内嵌servlet容器
    • 部署:jar—>运维—java -jar 启动

SpringBoot使用外部servlet

  • 修改pom.xml的打包方式,修改为war包形式
    在这里插入图片描述
    设置tomcat依赖设置scope为provided,让内嵌tomcat不参与打包
     <!--让它不参与打包部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency>
<type>pom</type> //用在父子类模块中,一般使用在父模块中,此时它相当于一个一个依赖管理(Maven Parent POM)文件
<optional>true</optional> //用在父模块中,此时子模块不会继承父模块的该依赖,true:不传递,false:传递
<scope>provided</scope> //让依赖不参与打包
  • 设置tomcat的启动类
    目的是tomcat启动的时候调用configure方法来启动springboot的启动类
/*** 当tomcat启动时就会调用configure方法,去启动springboot的启动类*/
public class TomcatStartSpringBoot extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(MyApplication.class);}
}
  • 添加本地tomcat
  • 测试-使用本地tomcat启动
    在这里插入图片描述

在这里插入图片描述

servlet容器-外部servlet启动SpringBoot原理

tomcat -->web.xml—filter servlet listener
tomcat不会主动去启动springboot应用,所以tomcat启动的时候肯定调用了SpringBootServletInitializer 的configure方法

public class TomcatStartSpringBoot extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(MyApplication.class);}
}

启动原理

在这里插入图片描述
当servlet容器启动的时候,就会去META-INF/service/文件下去找javax.servlet.ServletContainerInitializer文件
在这里插入图片描述
这个文件肯定绑定了一个ServletContainerInitializer

org.springframework.web.SpringServletContainerInitializer

当servlet容器启动的时候就会去该文件夹中找到ServletContainerInitializer的实现类SpringServletContainerInitializer从而创建它实例(SPI机制),并且调用onStartup方法

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {public SpringServletContainerInitializer() {}public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {List<WebApplicationInitializer> initializers = Collections.emptyList();Iterator var4;if (webAppInitializerClasses != null) {initializers = new ArrayList(webAppInitializerClasses.size());var4 = webAppInitializerClasses.iterator();while(var4.hasNext()) {Class<?> waiClass = (Class)var4.next();if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {((List)initializers).add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());} catch (Throwable var7) {throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);}}}}if (((List)initializers).isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");} else {servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort((List)initializers);var4 = ((List)initializers).iterator();while(var4.hasNext()) {WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();initializer.onStartup(servletContext);}}}
}
  • @HandlesTypes({WebApplicationInitializer.class})
  • @HandlesTypes({WebApplicationInitializer.class})传入的类为ServletContainerInitializer感兴趣的类
  • 容器会自动在classpath中找到WebApplicationInitializer 会传入到onStartup方法的webAppInitializerClasses 参数中,也包括了之前自定义的TomcatStartSpringBoot
    在这里插入图片描述
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements 
  • 启动WebApplicationInitializer的onStartup方法
    循环调用所有WebApplicationInitializer实例的onstartup方法
 if (((List)initializers).isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");} else {servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort((List)initializers);var4 = ((List)initializers).iterator();while(var4.hasNext()) {WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();//循环调用所有WebApplicationInitializer实例的onstartup方法initializer.onStartup(servletContext);}
  • 调用SpringBootServletInitializer onStartup方法
    public void onStartup(ServletContext servletContext) throws ServletException {servletContext.setAttribute("logging.register-shutdown-hook", false);this.logger = LogFactory.getLog(this.getClass());//创建对对对 ,这个方法里就会去调用configure方法WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);if (rootApplicationContext != null) {servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));} else {this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");}}
  • createRootApplicationContext
    在这里插入图片描述
    当调用configure方法的时候,就会调用我们自己的configure方法(为了更好的理解才去父类里看),因为TomcatStartSpringBoot是继承了SpringBootServletInitializer又继承了WebApplicationInitializer(我们自己定义的TomcatStartSpringBoot也是一个WebApplicationInitializer)
    在这里插入图片描述
public class TomcatStartSpringBoot extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(MyApplication.class);}
}

createRootApplicationContext
调用build方法,就会根据传入的SpringBoot的启动类来构建一个springapplication
在这里插入图片描述

  • SpringApplicationBuilder
    public SpringApplication build(String... args) {this.configureAsChildIfNecessary(args);this.application.addPrimarySources(this.sources);return this.application;}

最后再调用run方法来启动springboot
在这里插入图片描述
启动springboot

   protected WebApplicationContext run(SpringApplication application) {return (WebApplicationContext)application.run(new String[0]);}

相关文章:

  • vue2和vue3中实现点击复制粘贴功能
  • Java 泛型:上界通配符和下界通配符的用途和限制
  • 绝区零国际服怎么下载 绝区零国际服下载教程
  • 为什么有些人思考得多,决策反而不好?避免过度拟合的终极指南:决策高手的秘密:灰度认知,黑白决策
  • 什么是 API 代理?
  • 华为机试HJ3明明的随机数
  • 每日一练 - Routing Policy节点逻辑
  • ctfshow sql注入 web234--web241
  • Spring Security在企业级应用中的应用
  • 基于深度学习的虚拟换装
  • 【sqlite3】联系人管理系统
  • 昇思25天学习打卡营第7天|深度学习流程全解析:从模型训练到评估
  • 数据资产治理的智能化探索:结合云计算、大数据、人工智能等先进技术,探讨数据资产治理的智能化方法,为企业提供可靠、高效的数据资产解决方案,助力企业提升竞争力
  • selenium 获取请求头cookie信息
  • SerDes介绍以及原语使用介绍(4)ISERDESE2原语仿真
  • Android 控件背景颜色处理
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • es6要点
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • Median of Two Sorted Arrays
  • Vue.js源码(2):初探List Rendering
  • 阿里研究院入选中国企业智库系统影响力榜
  • 百度地图API标注+时间轴组件
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 第2章 网络文档
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 移动端 h5开发相关内容总结(三)
  • Java性能优化之JVM GC(垃圾回收机制)
  • ​io --- 处理流的核心工具​
  • #{}和${}的区别是什么 -- java面试
  • #Spring-boot高级
  • (1)SpringCloud 整合Python
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (转)http协议
  • (转)Linq学习笔记
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .net core 依赖注入的基本用发
  • .Net Core与存储过程(一)
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .netcore 获取appsettings
  • .NET和.COM和.CN域名区别
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • @FeignClient注解,fallback和fallbackFactory
  • [ element-ui:table ] 设置table中某些行数据禁止被选中,通过selectable 定义方法解决
  • [ 数据结构 - C++]红黑树RBTree
  • [1] 平面(Plane)图形的生成算法
  • [20170705]lsnrctl status LISTENER_SCAN1
  • [2021]Zookeeper getAcl命令未授权访问漏洞概述与解决
  • [Angular] 笔记 16:模板驱动表单 - 选择框与选项
  • [BZOJ 1032][JSOI2007]祖码Zuma(区间Dp)