一、注解和启动类SpringBootApplication

它是一个复式注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })

在以上的注解中,我们要关注的有三个注解:

1.1 @SpringBootConfiguration:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration { }

@Configuration注解

配置注解的功能是将当前类标注为配置类,并将当前类里以 @Bean 注解标记的方法的实例
注入到spring容器中,实例名即为方法名。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

看到这个 @Component 注解, 意味也将会注册为bean, 其内部也可以依赖注入。@Configuration类通常使用
AnnotationConfigApplicationContext或其支持Web的变体AnnotationConfigWebApplicationContext进行引导。
后续可以继续深入学习spring更底层的运行机制......

1.2 @EnableAutoConfiguration

@EnableAutoConfiguration 注解启用自动配置,其可以帮助SpringBoot应用将所有符合
条件的 @Configuration 配置都加载到当前 IoC 容器之中。@EnableAutoConfiguration 借助 AutoConfigurationImportSelector
的帮助,而后者通过实现 electImports()方法来导出Configuration。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

源码分析:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
} protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

AutoConfigurationImportSelector 类的 selectImports() 方法里面通过调用Spring Core 包里 SpringFactoriesLoader

类的 loadFactoryNames()方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}

从 ClassPath下扫描所有的 META-INF/spring.factories 配置文件,并将spring.factories 文件中的 EnableAutoConfiguration
对应的配置项通过反射机制实例化为对应标注了 @Configuration 的形式的IoC容器配置类,然后注入IoC容器。

1.3 @ComponentScan

组件扫描,可自动发现和装配Bean,功能其实就是自动扫描并加载符合条件的组件或者bean定义,最终将这些bean定义加
载到IoC容器中。可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认
Spring框架实现会从声明@ComponentScan所在类的package进行扫描。默认扫描SpringApplication的run方法里的
Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下。
对于该注解,还可以通过 basePackages 属性来更细粒度的控制该注解的自动扫描范围,比如:

@ComponentScan(basePackages = {"cn.shu.controller","cn.shu.entity"})

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

二、SpringApplication类

我们也需要先构造 SpringApplication 类对象,然后调用该对象的 run() 方法。那么接下来就讲讲 SpringApplication 的构造
过程 以及其 run() 方法的流程,搞清楚了这个,那么也就搞清楚了SpringBoot应用是如何运行起来的!

2.1 实例一个SpringApplication对象,根据类名构造参数。

看一下源码:

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 可以为空
this.resourceLoader = resourceLoader;
// 启动资源类不能为空
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用的类型:创建的是 REACTIVE应用、SERVLET应用、NONE 三种中的某一种
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化注册所有实现应用上下文初始化类,Application Context Initializers
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化注册所有实现ApplicationListener的类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获得当前执行main方法的类对象,作为启动类。
this.mainApplicationClass = deduceMainApplicationClass();
}
ApplicationContextInitializer是Spring框架中的接口,其作用可以理解为在ApplicationContext执行refresh之前,调用ApplicationContextInitializer的initialize()方法,
对ApplicationContext做进一步的设置和处理。spring-boot-xxx.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer。

ApplicationListener是Spring框架中的接口,就是事件监听器,其作用可以理解为在SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。
SpringBoot只提供了一个SpringApplicationRunListener的实现类,就是EventPublishingRunListener,起作用就是在SpringBoot启动过程中,负责注册ApplicationListener
监听器,在不同的时点发布不同的事件类型,如果有哪些ApplicationListener的实现类监听了这些事件,则可以接收并处理。
看看spring.factories的配置:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners,仅仅一个实现类
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener # Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers # Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer # Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener # Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor # Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer # FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

2.2 怎么区别应用类别和上下文:REACTIVE、SERVLET、NONE

SERVLET:{ "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
REACTIVE: private static final String WEBFLUX_INDICATOR_CLASS = "org."
+ "springframework.web.reactive.DispatcherHandler";
NONE:其他。 static WebApplicationType deduceFromApplicationContext(
Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}

2.3  获得所有ApplicationContextInitializer.class的实例,放入初始化容器中。

setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class)); UampServletInitializer extends SpringBootServletInitializer implements WebApplicationInitializer public class ServletContextApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

2.4 使用 SpringFactoriesLoader查找并加载 classpath下文件中的所有可用的 ApplicationListener。

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

三、简要分析SpringApplication类核心方法run()

3.1 源码如下:

public ConfigurableApplicationContext run(String... args) {
//spring-framework提供了一个StopWatch类可以做类似任务执行时间控制,也就是封装了一个对开始时间,结束时间记录操作的Java类
StopWatch stopWatch = new StopWatch();
stopWatch.start();//计时任务的开始
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
//之后逐个调用其started()方法,广播SpringBoot要开始执行了。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//如果为空,就设置ture. System.setProperty("spring.beaninfo.ignore", ignore.toString());
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
//根据webEnvironment的值来决定创建何种类型的ApplicationContext对象
//如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
//否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);
//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//初始化所有自动配置类,调用ApplicationContext的refresh()方法
refreshContext(context); afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
//如果开启日志,则答应执行是时间
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//开始对上下文监听
listeners.started(context);
//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
//该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,
//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//调用异常分析器打印报告,调用所有的SpringApplicationRunListener的finished()方法将异常信息发布出去
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
// 运行所有的监视器
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

3.2 流程图:

3.3 整个流程简要概括如下:

  • 通过this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
  • 获取并创建 SpringApplicationRunListener 对象.
  • 然后由 SpringApplicationRunListener 来发出 starting 消息
  • 创建参数,并配置当前 SpringBoot 应用将要使用的 Environment
  • 完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息
  • 初始化 ApplicationContext,并设置 Environment,加载相关配置等
  • 由 SpringApplicationRunListener 来发出 contextPrepared 消息,告知SpringBoot 应用使用的 ApplicationContext 已准备OK
  • 将各种 beans 装载入 ApplicationContext,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用使用的 ApplicationContext 已装填OK
  • refresh ApplicationContext,完成IoC容器可用的最后一步
  • 由 SpringApplicationRunListener 来发出 started 消息
  • 完成最终的程序的启动
  • 由 SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了

说明

  • SpringBoot的启动过程,实际上就是对ApplicationContext的初始化过程。
  • ApplicationContext创建后立刻为其设置Environmen,并由ApplicationContextInitializer对其进一步封装。
  • 通过SpringApplicationRunListener在ApplicationContext初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。
  • 初始化过程中完成IoC的注入,包括通过@EnableAutoConfiguration导入的各种自动配置类。
  • 初始化完成前调用ApplicationRunner和CommandLineRunner的实现类。

3.4 如果要对SpringApplication进行扩展,我们可以选择如下三种方案:

  1. 创建ApplicationContextInitializer的实现类
  2. 创建ApplicationListener的实现类
  3. 创建ApplicationRunner和CommandLineRunner的实现类

四、SpringBootServletInitializer

4.1继承SpringBootServletInitializer

SpringBootServletInitializer用于替代传统mvc模式中的web.xml。如果你要使用外部的sevvlet容器,例如tomcat。
就需要继承该类并重写configure方法。在创建springboot项目时如果你创建的是war,则系统会默认提供一个继承类。
如果创建时选择的是jar,系统不会提供这个继承!需要自己写这个继承。
如果不继承该类会怎么样呢?答:项目无法使用外部容器启动

如果重复继承会怎么样呢?答:项目可以启动,但是会遇到很多不可预期的问题

SpringBootServletInitializer就是一个org.springframework.web.context.WebApplicationContext,容器启动时会调用其onStartup(ServletContext servletContext)方法。

SpringBootServletInitializer的核心方法onStartup,里面最重要的方法是createRootApplicationContext(servletContext)。

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
......
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
public void contextInitialized(ServletContextEvent event) {
}
});
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
} }

4.2createRootApplicationContext方法的源码

SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,
并最终调用SpringApplication的run方法的过程。

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//创建SpringApplicationBuilder,并用其生产出SpringApplication对象
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
builder.main(this.getClass());
ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
}
//初始化并封装SpringApplicationBuilder对象,为SpringApplication对象增加ApplicationContextInitializer和ApplicationListener做准备
builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
//指定创建的ApplicationContext类型
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
//传递入口类,并构建SpringApplication对象
//可以通过configure()方法对SpringBootServletInitializer进行扩展
builder = this.configure(builder);
builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(this.getClass()));
} Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
//最后调用SpringApplication的run方法
return this.run(application);
}

4.3扩展SpringBootServletInitializer

与扩展SpringApplication类似,ApplicationContextInitializer和ApplicationListener可以基于SpringApplicationBuilder提供
的public方法进行扩展。

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
application.initializers(xxApplicationContextInitializer1,xxApplicationContextInitializer2);
application.listeners(xxApplicationListener1,xxApplicationListener2)
return application.sources(xxxApplication.class);
} }
 
 
 

SpringBoot的启动简述的更多相关文章

  1. SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用

    目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...

  2. springboot之启动原理解析

    前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...

  3. Springboot 项目启动后执行某些自定义代码

    Springboot 项目启动后执行某些自定义代码 Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRun ...

  4. SpringBoot的启动流程分析(1)

    通过分析我们可以找到 org.springframework.boot.SpringApplication 中如下, public static ConfigurableApplicationCont ...

  5. 深入理解SpringBoot之启动探究

    SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用.可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接 ...

  6. springBoot项目启动类启动无法访问

    springBoot项目启动类启动无法访问. 网上也查了一些资料,我这里总结.下不来虚的,也不废话. 解决办法: 1.若是maven项目,则找到右边Maven Projects --->Plug ...

  7. java框架之SpringBoot(10)-启动流程及自定义starter

    启动流程 直接从 SpringBoot 程序入口的 run 方法看起: public static ConfigurableApplicationContext run(Object source, ...

  8. SpringBoot项目启动时链接数据库很慢

    SpringBoot项目启动时链接数据库很慢 springboot项目在启动时候,如下图所示,链接数据库很慢 解决方法:在mysql 的配置文件中 配置 skip-name-resolve

  9. SpringBoot一站式启动流程源码分析

    一.前言 由上篇文章我们得知,SpringBoot启动时,就是有很简单的一行代码.那我们可以很清楚的看到这行代码的主角便是SpringApplication了,本文我们就来聊一聊这货,来探寻Sprin ...

随机推荐

  1. 正则表达式分组(Grouping)

    一 捕获型 (x) 匹配 x ,并且捕获匹配项 const regExp = /(\w+)\s+(\d+)/; const str = 'Android 8'; str.replace(regExp, ...

  2. IDEA激活—免费永久激活(lookdiv.com)

    网址: http://lookdiv.com/ 钥匙就是网址 钥匙:lookdiv.com 亲测有效!非常好用.

  3. 学习使用CGI和HTML

    目标和需求: (1)通过网页查询并设置开发板的网络参数,要求至少可查询IP地址.子网掩码.网关.MAC地址,可设置自动获取IP或固定IP,设置包括查询的内容 (2)使用CGI编程+HTML实现简单数据 ...

  4. 【Linux开发】arm-linux-gnueabihf-gcc下载

    原文地址:http://www.veryarm.com/arm-linux-gnueabihf-gcc veryarm是个不错的网站,里面介绍了很多相关的基础知识. arm-linux-gnueabi ...

  5. [知乎]ARM 到底是什么

    [小宅按]近期公司推出来基于ARM芯片的服务器,本文就一些基本概念,比如ARM, ARM64, ARMv8, ARM7,ARMv7, 64位等让人费解的概念进行了粗浅地分析,涉及的关键字已用粗体标出. ...

  6. Oracle SQL调优

    在多数情况下,Oracle使用索引t来更快地遍历表,优化器主要根据定义的索引来提高性能. 但是,如果在SQL语句的where子句中写的SQL代码不合理,就会造成优化器删去索引而使用全表扫描,一般就这种 ...

  7. merge into 导致序列跳号

    For each row merged by a MERGE statement. The reference to NEXTVAL can appear in the merge_insert_cl ...

  8. 嘉馨学姐又双叒叕来吃包子了 QDUOJ 模拟 尺度法

    嘉馨学姐又双叒叕来吃包子了 QDUOJ 模拟 尺度法 点我进入OJ题目详情 题意 给你一串数,让你求长度最长的子串,这个字串满足里面没有重复出现的数字. 解题思路 使用一个标记数组,来标记每个数的第一 ...

  9. windows 下mysql5.7设置密码

    学习Springboot时用到mysql数据库,以前用的mysql5.6版本 基本百度一个教程即可,听说5.7有新改动,突然想试试于是找到解压版mysql5.7照常安装, 以前用的mysql5.6版本 ...

  10. 深入理解 JavaScript中的变量、值、传参

    1. demo 如果你对下面的代码没有任何疑问就能自信的回答出输出的内容,那么本篇文章就不值得你浪费时间了. var var1 = 1 var var2 = true var var3 = [1,2, ...