深入springboot原理——一步步分析springboot启动机制(starter机制)

2023-05-16

前言

使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程。本文的目的就是一步步分析springboot的启动过程,分析springboot是如何帮我们简化这个过程的。

springboot帮我们做了什么

通常搭建一个基于spring的web应用,我们需要做以下工作:

1、pom文件中引入相关jar包,包括spring、springmvc、redis、mybaits、log4j、mysql-connector-java 等等相关jar ...

2、配置web.xml,Listener配置、Filter配置、Servlet配置、log4j配置、error配置 ...

3、配置数据库连接、配置spring事务

4、配置视图解析器

5、开启注解、自动扫描功能

6、配置完成后部署tomcat、启动调试

......

搭个初始项目不一会就一个小时甚至半天过去了。而用springboot后,一切都变得很简便快速。下来我们来一步步分析springboot的起步依赖与自动配置这两个核心原理。

回到顶部

起步依赖

在springboot中我们只需要引入下面简单的几步就可以完成一个ssm后台项目的初始搭建。

1、引入jar

复制代码


<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<!--mybatis 开发包-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>
<!--springboot web模块支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!--druid 的数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.31</version>
</dependency>  

复制代码

spring-boot-starter-web包自动帮我们引入了web模块开发需要的相关jar包,

mybatis-spring-boot-starter帮我们引入了dao开发相关的jar包。

spring-boot-starter-xxx是官方提供的starter,xxx-spring-boot-starter是第三方提供的starter。

如下截图:

可以看出在这个mybatis-spring-boot-starter 中,并没有任何源码,只有一个pom文件,它的作用就是帮我们引入了相关jar包。

2、配置数据源

复制代码


spring:
  datasource:
     url: jdbc:mysql://127.0.0.1:3306/mybatis_test
     username: root
     password: root
     driver-class-name: com.mysql.jdbc.Driver
     type: com.alibaba.druid.pool.DruidDataSource
     dbcp2:
       min-idle: 5
       initial-size: 5
       max-total: 5
       max-wait-millis: 200  

复制代码

 stater机制帮我们完成了项目起步所需要的的相关jar包。那问题又来了,传统的spring应用中不是要在application.xml中配置很多bean的吗,比如dataSource的配置,transactionManager的配置 ... springboot是如何帮我们完成这些bean的配置的?下面我们来分析这个过程

回到顶部

自动配置

基于java代码的bean配置

以mybatis为例,在上面的截图中,我们发下mybatis-spring-boot-starter这个包帮我们引入了mybatis-spring-boot-autoconfigure这个包,如下图:

里面有MybatisAutoConfiguration这个类,打开这个类看看有什么东西。

熟悉@Configuration&、@Bean这两个bean的同学或许已经知道了。这两个注解一起使用就可以创建一个基于java代码的配置类,可以用来替代相应的xml配置文件。

@Configuration注解的类可以看作是能生产让Spring IoC容器管理的Bean实例的工厂。

@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册到spring容器中。

 传统的基于xml的bean配置方法如下:


<beans>  
    <bean id = "car" class="com.itpsc.Car">  
        <property name="wheel" ref = "wheel"></property>  
    </bean>  
    <bean id = "wheel" class="com.itpsc.Wheel"></bean>  
</beans>  

相当于用基于java代码的配置方式:

复制代码


@Configuration  
public class Conf {  
    @Bean  
    public Car car() {  
        Car car = new Car();  
        car.setWheel(wheel());  
        return car;  
    }  
    @Bean   
    public Wheel wheel() {  
        return new Wheel();  
    }  
}  

复制代码

所以上面的MybatisAutoConfiguration这个类,自动帮我们生成了SqlSessionFactory这些Mybatis的重要实例并交给spring容器管理,从而完成bean的自动注册。

自动配置条件依赖

从MybatisAutoConfiguration这个类中使用的注解可以看出,要完成自动配置是有依赖条件的。

复制代码


@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
//....
}  

复制代码

这些是springboot特有的,常见的条件依赖注解有:

@ConditionalOnBean,仅在当前上下文中存在某个bean时,才会实例化这个Bean。

@ConditionalOnClass,某个class位于类路径上,才会实例化这个Bean。

@ConditionalOnExpression,当表达式为true的时候,才会实例化这个Bean。

@ConditionalOnMissingBean,仅在当前上下文中不存在某个bean时,才会实例化这个Bean。

@ConditionalOnMissingClass,某个class在类路径上不存在的时候,才会实例化这个Bean。

@ConditionalOnNotWebApplication,不是web应用时才会实例化这个Bean。

@AutoConfigureAfter,在某个bean完成自动配置后实例化这个bean。

@AutoConfigureBefore,在某个bean完成自动配置前实例化这个bean。

所以要完成Mybatis的自动配置,需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类,需要存在DataSource这个bean且这个bean完成自动注册。

进入DataSourceAutoConfiguration这个类,可以看到这个类属于这个包:

org.springframework.boot.autoconfigure.jdbc

这个包又属于spring-boot-autoconfigure-2.0.4.RELEASE.jar这个包,自动配置这个包帮们引入了jdbc、kafka、logging、mail、mongo等包。很多包需要我们引入相应jar后自动配置才生效。

bean参数获取

到此我们已经知道了bean的配置过程,但是还没有看到springboot是如何读取yml或者properites配置文件的的属性来创建数据源的?

在DataSourceAutoConfiguration类里面,我们注意到使用了EnableConfigurationProperties这个注解。

复制代码


@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
...
}  

复制代码

DataSourceProperties中封装了数据源的各个属性,且使用了注解ConfigurationProperties指定了配置文件的前缀。

复制代码


@ConfigurationProperties(
    prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    private ClassLoader classLoader;
    private String name;
    private boolean generateUniqueName;
    private Class<? extends DataSource> type;
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private String jndiName;
    ...
}  

复制代码

@EnableConfigurationProperties与@ConfigurationProperties这两个注解有什么用呢?我们先看一个例子:

复制代码


@Component
@ConfigurationProperties(prefix="spring.datasource")
public class PropertiesBean {
    private String url;
    private String username;
    private String password;
    //省略getter、setter...
    @Override
    public String toString() {
        return "PropertiesBean{" +
                "url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}  

复制代码

复制代码


@SpringBootApplication
@MapperScan("com.itpsc.mapper*")
@EnableConfigurationProperties
public class SpringbootMybatisDemoApplication {
    public static void main(String[] args) {
        //SpringApplication.run(SpringbootMybatisDemoApplication.class, args);
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootMybatisDemoApplication.class, args);
         //获取yml配置转换后的bean
        System.out.println("----------------------"+context.getBean(PropertiesBean.class));
        context.close();
    }
}  

复制代码

运行结果:

从运行结果可以看出@ConfigurationProperties与@EnableConfigurationPropertie的作用就是:

@ConfigurationProperties注解的作用是把yml或者properties配置文件转化为bean。

@EnableConfigurationProperties注解的作用是使@ConfigurationProperties注解生效。如果只配置@ConfigurationProperties注解,在spring容器中是获取不到yml或者properties配置文件转化的bean的。

通过这种方式,把yml或者properties配置参数转化为bean,这些bean又是如何被发现与加载的?

bean发现

springboot默认扫描启动类所在的包下的主类与子类的所有组件,但并没有包括依赖包的中的类,那么依赖包中的bean是如何被发现和加载的?

我们通常在启动类中加@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}
)}
)
public @interface SpringBootApplication {
...
}  

复制代码

实际上重要的只有三个Annotation:

@Configuration(@SpringBootConfiguration里面还是应用了@Configuration)

@EnableAutoConfiguration

@ComponentScan

@Configuration的作用上面我们已经知道了,被注解的类将成为一个bean配置类。

@ComponentScan的作用就是自动扫描并加载符合条件的组件,比如@Component和@Repository等,最终将这些bean定义加载到spring容器中。

@EnableAutoConfiguration 这个注解的功能很重要,借助@Import的支持,收集和注册依赖包中相关的bean定义。

复制代码


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}  

复制代码

如上源码,@EnableAutoConfiguration注解引入了@AutoConfigurationPackage和@Import这两个注解。@AutoConfigurationPackage的作用就是自动配置的包,@Import导入需要自动配置的组件。

进入@AutoConfigurationPackage,发现也是引入了@Import注解

复制代码


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

复制代码

复制代码


static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, new String[]{(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()});
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    }  

复制代码

new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()

new AutoConfigurationPackages.PackageImport(metadata)

这两句代码的作用就是加载启动类所在的包下的主类与子类的所有组件注册到spring容器,这就是前文所说的springboot默认扫描启动类所在的包下的主类与子类的所有组件。

那问题又来了,要搜集并注册到spring容器的那些beans来自哪里?

进入 AutoConfigurationImportSelector类,

复制代码


public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NO_IMPORTS = new String[0];
...
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if(!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
        }
    }
...

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

...

}  

复制代码

SpringFactoriesLoader.loadFactoryNames方法调用loadSpringFactories方法从所有的jar包中读取META-INF/spring.factories文件信息。

复制代码


private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap result = (MultiValueMap)cache.get(classLoader);
        if(result != null) {
            return result;
        } else {
            try {
                Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result1 = new LinkedMultiValueMap();

                while(ex.hasMoreElements()) {
                    URL url = (URL)ex.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry entry = (Entry)var6.next();
                        List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result1.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result1);
                return result1;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }  

复制代码

下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容,其中有一个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定义了需要自动配置的bean,通过读取这个配置获取一组@Configuration类。

复制代码


org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\  

复制代码

每个xxxAutoConfiguration都是一个基于java的bean配置类。实际上,这些xxxAutoConfiguratio不是所有都会被加载,会根据xxxAutoConfiguration上的@ConditionalOnClass等条件判断是否加载。

复制代码


private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
        try {
            Class ex = ClassUtils.forName(instanceClassName, classLoader);
            if(!factoryClass.isAssignableFrom(ex)) {
                throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(ex, new Class[0]).newInstance(new Object[0]);
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
        }
    }  

复制代码

 如上代码段,通过反射机制将spring.factories中@Configuration类实例化为对应的java实列。到此我们已经知道怎么发现要自动配置的bean了,最后一步就是怎么样将这些bean加载到spring容器。

bean加载

如果要让一个普通类交给Spring容器管理,通常有以下方法:

1、使用 @Configuration与@Bean 注解

2、使用@Controller @Service @Repository @Component 注解标注该类,然后启用@ComponentScan自动扫描

3、使用@Import 方法

springboot中使用了@Import 方法

@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解,AutoConfigurationImportSelector实现了DeferredImportSelector接口,

DeferredImportSelector接口继承了ImportSelector接口,ImportSelector接口只有一个selectImports方法。

复制代码


public class AutoConfigurationImportSelector implements DeferredImportSelector{
...
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if(!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
        }
}
...
}  

复制代码

复制代码


public interface DeferredImportSelector extends ImportSelector {
    @Nullable
    default Class<? extends DeferredImportSelector.Group> getImportGroup() {
        return null;
}
public interface Group {...}
}  

复制代码


public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}  

 我们先通过一个简单例子看看@Import注解是如何将bean导入到spring容器的。

1、新建一个bean

复制代码


public class User {
    private Long id;
    private String name;
    private String password;
private String phone;
...
}  

复制代码

2、创建一个ItpscSelector类继承ImportSelector接口并实现selectImports方法


public class ItpscSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.itpsc.entity.User"};
    }
}  

3、创建ImportConfig类,使用@Configuration、@Import(ItpscSelector.class)注解。


@Configuration
@Import(ItpscSelector.class)
public class ImportConfig {
}  

4、从容器获取bean

复制代码


@RunWith(SpringRunner.class)
@SpringBootTest
public class ImportSelectorTests {
    @Test
    public void testSelectImport() {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(ImportConfig.class);
        String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
    }
}  

复制代码

运行结果:

复制代码


org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
com.itpsc.entity.User  

复制代码

很直观,selectImports方法返回一组bean,@EnableAutoConfiguration注解借助@Import注解将这组bean注入到spring容器中,springboot正式通过这种机制来完成bean的注入的。

回到顶部

总结

我们可以将自动配置的关键几步以及相应的注解总结如下:

1、@Configuration&与@Bean->基于java代码的bean配置

2、@Conditional->设置自动配置条件依赖

3、@EnableConfigurationProperties与@ConfigurationProperties->读取配置文件转换为bean。

4、@EnableAutoConfiguration、@AutoConfigurationPackage 与@Import->实现bean发现与加载。

原文链接:深入springboot原理——一步步分析springboot启动机制(starter机制) - ITPSC - 博客园

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入springboot原理——一步步分析springboot启动机制(starter机制) 的相关文章

  • 使用jmx exporter采集kafka指标

    x1f680 优质资源分享 x1f680 学习路线指引 xff08 点击解锁 xff09 知识定位人群定位 x1f9e1 Python实战微信订餐小程序 x1f9e1 进阶级本课程是python flask 43 微信小程序的完美结合 xf
  • vscode启动项目EsLint报错(.eslintrc.js文件)

    root true 当前配置为根配置 xff0c 将不再从上级文件夹查找配置 parserOptions parser 39 babel eslint 39 采用 babel eslint 作为语法解析器 sourceType 39 mod
  • VSCode在扩展商店搜索时会出现 “提取扩展时出错。XHR failed”的错误

    之前装过一些外网的软件 它的局域网端口是11223 之后不再用了的时候卸载了它 但发现VSCode在扩展商店搜索时会出现 提取扩展时出错 XHR failed 的错误 还有微信小程序开发时也会无法访问接口 提示 11223 什么的 具体的忘
  • Kali WIndows 漏洞利用基础篇 (探索目标主机漏洞)

    通过使用Kali主机扫描工具Nmap和漏洞利用工具Msf来发现目标主机的系统漏洞和软件服务漏洞 因为很多教程只说了用什么漏洞去攻击 xff0c 但是前提是该主机存在漏洞或者是该漏洞未被安装补丁 xff08 该Kali已经实体化在笔记本电脑上
  • 通过KALI发起拒绝攻击

    通过使用Hping3发发起拒绝服务攻击 xff08 使得目标主机瘫痪 xff09 成立攻击组织 xff0c 搭建攻击网络环境 kali IP 10 117 10 111 目标主机 IP 10 117 0 15 使用工具Hping3 该工具使
  • Genymotion ova官方下载地址

    Genymotion ova包官方下载链接 amp 找寻方法 下载链接genymotion log 中获取下载链接 下载链接 https dl genymotion com dists 9 0 ova genymotion vbox86p
  • Ubuntu如何安装最新版安装gcc

    GCC GNU编译器集合 xff09 许多 C C 43 43 GNU工具和大多数的开源项目 xff0c 包括linux内核都是由GCC编译而来 那么今天就针对Ubuntu Linux安装最新版的GCC做讲解 注意 xff1a 为了保证成功
  • 使用Linux可视化远程工具Remmina来实现远程桌面共享(RDPP快速平台部署计划)

    Remmina 功能不在阐述 xff0c 如果你想远程共享桌面 xff0c Remmina 绝对是一个不错的工具 xff0c 我指的不是它有多强大 xff0c 而是它再强大的同时还提供了比较良好的用户界面 xff0c 并且简化了远程桌面的操
  • Linux Sleep命令暂停Bash脚本

    Bash脚本在Linux在为寻常不过了 xff0c 就如Windows上的bat文件一样 xff0c 那么有些人在运行bash脚本时 xff0c 循环和想暂停怎么做这就要用到Linux中的 sleep命令 xff0c 故名思意sleep x
  • 与其他数据库相比,MySQL的特点

    与其他数据库系统相比 xff0c MySQL有点与众不同 1 MySQL并不完美 xff0c 却足够灵活 xff0c 能够适应高要求的环境 xff0c 例如Web类应用 2 MySql既可以嵌入式到应用程序中 xff0c 也可以支持数据仓库
  • MySQL服务器逻辑架构分析

    MySQL逻辑架构大致分为三层 最上层 xff0c 大多数基于网络的客户端 服务器的工具或者服务都有类似的架构 比如链接处理 授权认证 安全等等 第二层架构 xff0c 大多数MySQL的核心服务功能都在这一层 xff0c 所有跨存储引擎的
  • (一)Android与Java语言有什么关系?

    Java编程语言与Java平台是两个完全不同的概念 前者泛指一系列编程的语法 xff0c 而后者包括前者 xff0c 同时又超出前者的范围 一般而言 xff0c Java平台由三部分组成 xff0c 分为核心的Java API xff08
  • 记一次因gstreamer安装错误导致的系统报错

    问题出现 xff1a 前一天无脑安装gstreamer xff0c 按照网上杂七杂八的教程一通乱装 xff0c 最后死于qt gstreamer安装 第二天发现 xff0c 进入系统设置后 xff0c 点击 详细信息 无效 xff1a 第一
  • Kotlin扩展插件 kotlin-android-extensions

    Kotlin扩展插件 kotlin android extensions 在学习第一行代码第三版的时候 xff0c 发现书中有提到 kotlin android extensions这个插件 xff0c 可以直接使用布局中的控件id来操作v
  • linux软件管理

    1 使用网络安装资源安装软件 1 切换目录 cd etc yum repos d 2 建立文件 vim westos repo 文件必须以 repo结尾 redhat 仓库名称 name 61 haha 对软件源的描述 baseurl 61
  • 移除JSONArray中匹配的元素

    移除JSONArray中匹配的元素 helper isBookOrChange获取类中isBookOrChange值 64 param jarr 64 return 64 author taiyang public JSONArray ge
  • 移动平均法又称滑动平均法、滑动平均模型法(Moving average,MA)

    转自http jingji 100xuexi com view otdetail 20130625 230f09b0 6e36 473b 8830 7f2b873a5252 html 什么是移动平均法 移动平均法是用一组最近的实际数据值来预
  • centos7 结束多pid同名进程脚本文件

    centos7 结束多pid同名进程脚本文件 创建脚本 vi k sh 输入脚本内容 PIDS 61 96 ps e awk 39 0 9 0 9 0 9 39 1 39 printf 34 d 34 1 39 96 echo PIDS k
  • Docker之Linux(Centos)安装

    系统 Docker官网 Home Docker 关闭防火墙和关闭SELinux都是因为我使用虚拟机安装了纯净的centos系统 非必需 关闭防火墙 systemctl stop firewalld service 关闭防火墙 systemc
  • 微信小程序开发工具格式化代码快捷键以及更改快捷键

    Alt 43 Shift 43 f 如果不喜欢默认可以自定义更改 注意事项

随机推荐

  • 最新UNI-APP 安卓本地(离线)打包(图文详细教程)

    uni app 官方文档地址 原生开发者支持 Android Studio 下载地址 xff1a Download Android Studio and SDK tools Android Developers App离线SDK下载 xff
  • Git 命令行学习笔记( 图解 )

    Git 官网 分布式版本控制工具 可以团队协作合作开发 代码合并等 github 和 国产 码云gitee 都是代码托管工具就是远程仓库 下载可以去官网或者去腾讯软件中心下载 官网有时候下载很慢 腾讯软件中心 如果有腾讯电脑管家直接管家下载
  • Laravel安装与初始化

    下载Laravel 使用 composer 安装 首先composer切换成中国镜像 下载更快 composer config g repo packagist composer https packagist phpcomposer co
  • 虚拟机安装Centos图文安装教程

    清华大学开源软件镜像站 Tsinghua Open Source Mirror 清华镜像 http mirrors aliyun com 阿里云镜像 vm虚拟机下载 VMware虚拟机中文版官方下载 虚拟机 华军软件园 虚拟机 安装 错误1
  • 虚拟机LNMP图文安装教程(一)

    xshell 链接虚拟机 centos 7 ip addr 查看本地IP地址 安装LNMP LNMP 官网 LNMP一键安装包 CentOS RadHat Debian Ubuntu下自动编译安装Nginx PHP MySQL PHPMyA
  • 虚拟机LNMP操作记录(二)

    netstat nltp 查看当前端口 LNMP php多版本 lnmp文件目录里 运行 install sh mphp MySQL连接 更换环境变量的PHP版本 cd usr bin ll grep php rm rf php php f
  • Deep Learning 最优化方法之Momentum(动量)

    本文是Deep Learning 之 最优化方法系列文章的Momentum xff08 动量 xff09 方法 主要参考Deep Learning 一书 整个优化系列文章列表 xff1a Deep Learning 之 最优化方法 Deep
  • phpStudy redis设置密码

    使用框架 链接redis时 xff0c 报错 xff1a AUTH 96 failed ERR Client sent AUTH but no password is set tcp 127 0 0 1 6379 解决方案一 xff1a 解
  • Swoole小练习之Tcp传输

    话不多说直接上代码 注意 我使用的端口都是一台服务器上的 具体实现应该是跨服务器的 通过Tcp传输 客户端 同步客户端 client 61 new Swoole Client SWOOLE SOCK TCP if client gt con
  • php workerman入门之运行起来

    如果你有服务器的话可以使用服务器操作或者学习 如果没有请安装一个虚拟机并安装php环境 可以参考我之前的文章 Windows安装虚拟机图文安装教程 山山河川的博客 CSDN博客 虚拟机LNMP图文安装教程 一 山山河川的博客 CSDN博客
  • php workerman入门之搭建websocket服务

    依旧参考官网 简单的开发示例 workerman手册 示例二 lt php use Workerman Worker use Workerman Connection TcpConnection require once DIR vendo
  • workerman+TP6实战网站客服系统之项目初始化

    TP6 官方手册 安装 ThinkPHP6 0完全开发手册 看云 安装TP6 composer create project topthink think tp6 nbsp 报错 nbsp 解决问题 PHP默认把这个 proc open 函
  • 虚拟机LNMP操作记录(三)

    nginx配置多站点之端口访问 查看nginx conf文件 lnmp 已经默认添加了引入vhost下所有文件的准备 复制server里面的内容 server listen 80 default server reuseport liste
  • workerman+TP6实战网站客服系统之前端页面部署

    前端演示效果 主攻后端和workman所以本次前端页面比较简单 后续会有聊天高级版的教程敬请期待 Layui下载 Layui 经典开源模块化前端 UI 组件库 nbsp nbsp 粘贴到tp6项目里面 nbsp 然后创建模板页面
  • PHP多进程(一)之pcntl_fork

    知识来源 nbsp nbsp nbsp nbsp 知识无价 拒绝白嫖 Linux下PHP多进程编程 共44课时 PHP课程 51CTO学堂 多进程的作用是一个程序启动多个进程 一个程序启动起来本应该是一个进程 但它可作为父进程启动多个子进程
  • PHP多进程(二)之pcntl_wait

    知识来源 nbsp nbsp nbsp nbsp 知识无价 拒绝白嫖 Linux下PHP多进程编程 共44课时 PHP课程 51CTO学堂 上篇文章我们说到父进程应该回收子进程结束之后产生的数据 这样才会不浪费系统资源 一个程序启动之后 变
  • PHP前后分离接口加密探讨( AES+RSA )

    参考文章 php之RSA加密解密 小吴 斌的博客 CSDN博客 php rsa加密 RSA和AES的区别 LC超人在良家的博客 CSDN博客 aes rsa PHP实现非对称加密的方法 私钥及公钥加密解密的方法 php 公钥 私钥 Lord
  • Deep Learning 最优化方法之RMSProp

    本文是Deep Learning 之 最优化方法系列文章的RMSProp方法 主要参考Deep Learning 一书 整个优化系列文章列表 xff1a Deep Learning 之 最优化方法 Deep Learning 最优化方法之S
  • PHP多进程(三) 理解多进程

    知识来源 nbsp nbsp nbsp nbsp 知识无价 拒绝白嫖 Linux下PHP多进程编程 共44课时 PHP课程 51CTO学堂 本篇是一个过渡篇 重在理解多进程 以及进程执行过程和进程执行后的数据 nbsp 废话不多说直接上代码
  • 深入springboot原理——一步步分析springboot启动机制(starter机制)

    前言 使用过springboot的同学应该已经知道 xff0c springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程 本文的目的就是一步步分析springboot的启动过程 xff0c 分析sprin