Spring 入门

文章源码

Spring 概述

Spring

Spring 是分层的 Java SE/EE 应用全栈式轻量级开源框架,以 IOC(Inverse Of Control,反转控制)和 AOP(Aspect Oriented Programming,面向切面编程)为内核,提供了 表现层 Spring MVC 和 持久层 Spring JDBC 以及 业务层事务管理等众多技术。而且可以方便的整合其他开源框架和类库。

Spring 优势

  • 方便解耦,简化开发:通过 IOC 容器可以将对象间的依赖关系交由 Spring 进行控制,可以避免过度的程序耦合。而且也不需要再为单例模式、属性解析等底层需求编写代码。
  • 面向切面:通过 AOP,可以方便进行面向切面的编程,弥补面向对象的一切缺陷。
  • 声明式事务:通过 声明式方式灵活的进行事务管理,可以提高开发效率和质量。
  • 源码学习典范:Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码是 Java 技术的最佳实践的范例。

Spring 体系结构

程序的耦合和解耦

耦合性是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。

耦合的分类

  • 内容耦合:当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时。内容耦合是最高程度的耦合,应该避免使用。
  • 公共耦合:两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
  • 外部耦合:一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
  • 控制耦合:一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
  • 标记耦合:若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间存在一个标记耦合。
  • 数据耦合:模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
  • 非直接耦合:两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。

总结起来,就是如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。

程序耦合举例

  • AccountDAOImpl.java

    package cn.parzulpan.dao;
    
    /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 账户持久层接口的实现类
    */ public class AccountDAOImpl implements AccountDAO{
    /**
    * 模拟保存账户
    */
    public void saveAccount() {
    System.out.println("保存了账户...");
    }
    }
  • AccountServiceImpl.java

    package cn.parzulpan.service;
    
    import cn.parzulpan.dao.AccountDAO;
    import cn.parzulpan.dao.AccountDAOImpl; /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 账户业务层接口的实现类
    */ public class AccountServiceImpl implements AccountService{
    private AccountDAO accountDAO = new AccountDAOImpl(); // 这里发生了耦合 /**
    * 模拟保存账户
    */
    public void saveAccount() {
    accountDAO.saveAccount();
    }
    }
  • Client.java

    package cn.parzulpan.ui;
    
    import cn.parzulpan.service.AccountServiceImpl;
    
    /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 模拟一个表现层,用于调用业务层,实际开发中应该是一个 Servlet 等
    */ public class Client {
    public static void main(String[] args) {
    AccountServiceImpl accountService = new AccountServiceImpl(); // 这里发生了耦合
    accountService.saveAccount();
    }
    }

Factory 解耦

在实际开发中可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并且存起来(用容器存储)。在接下来的使用的时候,直接拿过来用就行。

那么,这个读取配置文件,创建和获取三层对象的类就是工厂类。即两个步骤:

  • 通过读取配置文件来获取创建对象的全限定类名。
  • 使用反射来创建对象,避免使用 new 关键字。

工厂就是负责给从容器中获取指定对象的类,这时候获取对象的方式发生了改变。之前,在获取对象时,采用 new 的方式是主动的。现在,在获取对象时,采用跟工厂要的方式,工厂会查找或者创建对象,是被动的

之前

现在

  • bean.properties

    accountService=cn.parzulpan.service.AccountServiceImpl
    accountDAO = cn.parzulpan.dao.AccountDAOImpl
  • BeanFactory.java

    package cn.parzulpan.factory;
    
    import java.io.InputStream;
    import java.util.*; /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 工厂类,负责给从容器中获取指定对象的类
    */ public class BeanFactory {
    private static Properties properties; private static Map<String, Object> beans; // Factory 解耦的优化,存放创建的对象,称为容器 static {
    try {
    // 实例化对象
    properties = new Properties();
    // 获取文件流对象,使用类加载器
    InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
    properties.load(is); beans = new HashMap<>();
    Enumeration<Object> keys = properties.keys();
    while (keys.hasMoreElements()) {
    String key = keys.nextElement().toString();
    String beanPath = properties.getProperty(key);
    Object instance = Class.forName(beanPath).newInstance();
    beans.put(key, instance);
    }
    } catch (Exception e) {
    e.printStackTrace();
    throw new ExceptionInInitializerError("初始化 Properties 失败!");
    }
    } /**
    * 获取指定对象的类
    * @param beanName
    * @return
    */
    public static Object getBean(String beanName){
    try {
    // return Class.forName(properties.getProperty(beanName)).newInstance(); // 两个步骤
    System.out.println(beanName + " " + beans.get(beanName));
    return beans.get(beanName); // 两个步骤
    } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }
    }
  • AccountServiceImpl.java

    package cn.parzulpan.service;
    
    import cn.parzulpan.dao.AccountDAO;
    import cn.parzulpan.dao.AccountDAOImpl;
    import cn.parzulpan.factory.BeanFactory; /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 账户业务层接口的实现类
    */ public class AccountServiceImpl implements AccountService{
    // private AccountDAO accountDAO = new AccountDAOImpl(); // 这里发生了耦合 /**
    * 模拟保存账户
    */
    public void saveAccount() {
    AccountDAO accountDAO = (AccountDAO) BeanFactory.getBean("accountDAO"); // 通过 Factory 解耦
    if (accountDAO != null) {
    accountDAO.saveAccount();
    }
    }
    }
  • Client.java

    package cn.parzulpan.ui;
    
    import cn.parzulpan.factory.BeanFactory;
    import cn.parzulpan.service.AccountService;
    import cn.parzulpan.service.AccountServiceImpl; /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 模拟一个表现层,用于调用业务层,实际开发中应该是一个 Servlet 等
    */ public class Client {
    public static void main(String[] args) {
    // AccountServiceImpl accountService = new AccountServiceImpl(); // 这里发生了耦合 AccountService accountService = (AccountService) BeanFactory.getBean("accountService"); // 通过 Factory 解耦 if (accountService != null) {
    accountService.saveAccount();
    } // 通过 Factory 解耦存在的问题
    for (int i = 0; i < 5; ++i) {
    System.out.println(BeanFactory.getBean("accountService")); // 对象被创建多次
    }
    }
    }

IOC 解耦

  • bean.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 把对象的创建交给 Spring 来管理-->
    <bean id="accountService" class="cn.parzulpan.service.AccountServiceImpl"/>
    <bean id="accountDAO" class="cn.parzulpan.dao.AccountDAOImpl"/> </beans>
  • ClientIOC.java

    package cn.parzulpan.ui;
    
    import cn.parzulpan.dao.AccountDAO;
    import cn.parzulpan.service.AccountService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext; /**
    * @Author : parzulpan
    * @Time : 2020-12
    * @Desc : 使用 IOC
    */ public class ClientIOC {
    public static void main(String[] args) {
    // 使用 ApplicationContext 接口,获取 Spring 核心容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    // 根据 id 获取 Bean 对象
    AccountService as = ac.getBean("accountService", AccountService.class);
    System.out.println(as);
    AccountDAO ad = ac.getBean("accountDAO", AccountDAO.class);
    System.out.println(ad);
    } }

IOC

IOC(Inverse Of Control,反转控制),把创建对象的权利交给 Spring 框架,它包括 DI(Dependency Injection,依赖注入)和 DL(Dependency Lookup,依赖查找)。

简单的说,IOC 是一种以被动接收的方式获取对象的思想,它主要是为了降低程序的耦合

bean 标签

  • 作用:用于配置对象让 Spring 来创建。默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
  • 属性
    • id:给对象在容器中提供一个唯一标识,用于获取对象
    • class:指定类的全限定类名,用于反射创建对象,默认情况下调用无参构造函数。
    • scope:指定对象的作用范围
      • singleton 默认值,单例的
      • prototype 多例的
      • request WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
      • session WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
      • global session WEB 项目中,应用在集群环境,如果没有集群环境那么 globalSession 相当于 session
    • init-method:指定类中的初始化方法名称。
    • destroy-method:指定类中销毁方法名称。

bean 的三种创建方式

第一种方式:使用默认无参构造函数。它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。

    <bean id="accountServiceIOC" class="cn.parzulpan.service.AccountServiceImplIOC"/>
<bean id="accountDAOIOC" class="cn.parzulpan.dao.AccountDAOImplIOC"/>

第二种方式:使用实例工厂的方法创建对象。先把工厂的创建交给 Spring 来管理,然后在使用工厂的 bean 来调用里面的方法。

  • factory-bean 属性:用于指定实例工厂 bean 的 id
  • factory-method 属性:用于指定实例工厂中创建对象的方法
package cn.parzulpan.factory;

import cn.parzulpan.service.AccountService;
import cn.parzulpan.service.AccountServiceImplIOC; /**
* @Author : parzulpan
* @Time : 2020-12
* @Desc : Spring 管理实例工厂。模拟一个工厂类,该类可能存在于 jar 包中,无法通过修改源码来提供默认构造函数
*/ public class InstanceFactory {
public AccountService getAccountService() {
return new AccountServiceImplIOC();
}
}
    <bean id="instanceFactory" class="cn.parzulpan.factory.InstanceFactory"/>
<bean id="accountServiceIOC" factory-bean="instanceFactory" factory-method="getAccountService"/>
<bean id="accountDAOIOC" class="cn.parzulpan.dao.AccountDAOImplIOC"/>

第三种方式:使用静态工厂的方法创建对象。使用某个类中的静态方法创建对象,并存入 Spring 核心容器。

  • id 属性:指定 bean 的 id,用于从容器中获取
  • class 属性:指定静态工厂的全限定类名
  • factory-method 属性:指定生产对象的静态方法
package cn.parzulpan.factory;

import cn.parzulpan.service.AccountService;
import cn.parzulpan.service.AccountServiceImplIOC; /**
* @Author : parzulpan
* @Time : 2020-12
* @Desc : Spring 管理静态工厂。模拟一个工厂类,该类可能存在于 jar 包中,无法通过修改源码来提供默认构造函数
*/ public class StaticFactory {
public static AccountService getAccountService() {
return new AccountServiceImplIOC();
}
}
    <bean id="accountServiceIOC" class="cn.parzulpan.factory.StaticFactory" factory-method="getAccountService"/>
<bean id="accountDAOIOC" class="cn.parzulpan.dao.AccountDAOImplIOC"/>

测试 ClientIOC.java:

public class ClientIOC {
public static void main(String[] args) {
// 使用 ApplicationContext 接口,获取 Spring 核心容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); System.out.println("------"); //
AccountService asi = ac.getBean("accountServiceIOC", AccountService.class);
System.out.println(asi);
AccountDAO adi = ac.getBean("accountDAOIOC", AccountDAO.class);
System.out.println(adi);
adi.saveAccount(); } }

bean 的作用范围和生命周期

对于单例对象scope="singleton"

一个应用只有一个对象的实例,它的作用范围就是整个引用。

生命周期

  • 对象出生:当应用加载,创建容器时,对象就被创建了。
  • 对象活着:只要容器在,对象一直活着。
  • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
    <!-- bean 的作用范围和生命周期 -->
<bean id="accountServiceIOC" class="cn.parzulpan.service.AccountServiceImplIOC" scope="singleton"
init-method="init" destroy-method="destroy"/>
<bean id="accountDAOIOC" class="cn.parzulpan.dao.AccountDAOImplIOC" scope="singleton"
init-method="init" destroy-method="destroy"/>

对于多例对象scope="prototype"

每次访问对象时,都会重新创建对象实例。

生命周期

  • 对象出生:当使用对象时,创建新的对象实例。
  • 对象活着:只要对象在使用中,就一直活着。
  • 对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。
    <bean id="accountServiceIOC" class="cn.parzulpan.service.AccountServiceImplIOC" scope="prototype"
init-method="init" destroy-method="destroy"/>
<bean id="accountDAOIOC" class="cn.parzulpan.dao.AccountDAOImplIOC" scope="prototype"
init-method="init" destroy-method="destroy"/>

测试

        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService asi = ac.getBean("accountServiceIOC", AccountService.class);
System.out.println(asi);
AccountDAO adi = ac.getBean("accountDAOIOC", AccountDAO.class);
System.out.println(adi);
adi.saveAccount(); ac.close(); // 手动关闭容器

DI

DI(Dependency Injection,依赖注入),它是 Spring IOC 的具体实现。因为 IOC 作用是降低耦合,那么依赖关系的维护都交给了 Spring,依赖关系的维护就称之为依赖注入。

能依赖注入的数据,有三类:

  • 基本数据类型和 String
  • 其他 Bean 类型,在配置文件中或者其他注解配置过的 Bean
  • 集合类型

依赖注入的方法,有三种:

  • 使用构造函数注入
  • 使用 set 方法注入
  • 使用注解注入

使用构造函数注入

类中需要提供一个对应参数列表的构造函数。

属性:

  • index 指定参数在构造函数参数列表的索引位置
  • type 指定参数在构造函数中的数据类型
  • name 指定参数在构造函数中的名称
  • value 它能赋的值是基本数据类型和 String 类型
  • ref 它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
  • 前三个都是找给谁赋值,后两个指的是赋什么值的
    <!-- 构造函数注入
类中需要提供一个对应参数列表的构造函数
属性:
index 指定参数在构造函数参数列表的索引位置
type 指定参数在构造函数中的数据类型
name 指定参数在构造函数中的名称
value 它能赋的值是基本数据类型和 String 类型
ref 它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
前三个都是找给谁赋值,后两个指的是赋什么值的
-->
<bean id="accountServiceDI" class="cn.parzulpan.service.AccountServiceImplDI">
<constructor-arg name="name" value="parzulpan"/>
<constructor-arg name="age" value="100"/>
<constructor-arg name="birthday" ref="now"/>
</bean>
<bean id="now" class="java.util.Date"/>

ClientDI.java

package cn.parzulpan.ui;

import cn.parzulpan.service.AccountService;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @Author : parzulpan
* @Time : 2020-12
* @Desc :
*/ public class ClientDI {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService asi = ac.getBean("accountServiceDI", AccountService.class);
System.out.println(asi);
asi.saveAccount(); // call saveAccount() parzulpan 100 Sun Dec 20 19:27:49 CST 2020
}
}

这种注入方式的优点:在获取 bean 对象时,注入数据是必须的操作,否则无法创建成功。

缺点:改变了 bean 对象的实例化方式,在创建对象时,如果用不到这些属性,也必须提供。

使用 set 方法注入

类中需要提供属性的 set 方法。

属性:

  • name:找的是类中 set 方法后面的部分
  • ref:给属性赋值是其他 bean 类型的
  • value:给属性赋值是基本数据类型和 string 类型的
    <!-- set 方法 注入
类中需要提供属性的 set 方法
属性:
name:找的是类中 set 方法后面的部分
ref:给属性赋值是其他 bean 类型的
value:给属性赋值是基本数据类型和 string 类型的
-->
<bean id="accountServiceDI2" class="cn.parzulpan.service.AccountServiceImplDI2">
<property name="name" value="库里"/>
<property name="age" value="30"/>
<property name="birthday" ref="nowSet"/>
</bean>
<bean id="nowSet" class="java.util.Date"/>

ClientDI.java

        AccountService asi2 = ac.getBean("accountServiceDI2", AccountService.class);
System.out.println(asi2);
asi2.saveAccount(); // call saveAccount() 库里 30 Sun Dec 20 20:11:01 CST 2020

这种注入方式的优点:创建对象时没有明确的限制,可以直接使用默认构造函数。

缺点:如果某个成员必须有值,则 set 方法无法保证一定执行。

但是,set 方式是更常用的方式。

注入集合属性

注入集合属性,在注入集合数据时,只要结构相同,标签可以互换。

List 结构的:array, list, set

Map 结构的:map, entry, props, prop

    <!-- 注入集合属性
在注入集合数据时,只要结构相同,标签可以互换
List 结构的:array, list, set
Map 结构的:map, entry, props, prop
-->
<bean id="accountServiceDI3" class="cn.parzulpan.service.AccountServiceImplDI3">
<property name="myStr">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="aaa"/>
<entry key="testB" value="bbb"/>
</map>
</property>
<property name="myProps">
<props>
<prop key="testA">aaa</prop>
<prop key="testB">bbb</prop>
</props>
</property>
</bean>

练习和总结


ApplicationContext 接口的三个实现类?

  • ClassPathXmlApplicationContext 它是从类的根路径下加载配置文件,推荐使用这种
  • FileSystemXmlApplicationContext 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置,不推荐使用这种
  • AnnotationConfigApplicationContext 使用注解配置容器对象时,需要使用此类来创建 Spring 核心容器,它用来读取注解

BeanFactory 和 ApplicationContext 的区别?

  • BeanFactory 是 Spring 核心容器中的顶层接口
  • ApplicationContext 是 BeanFactory 的子接口
  • 它们两者创建对象的时间点不一样
    • ApplicationContext 立即加载,只要读取了配置文件,默认情况下就会创建对象,适用于单例对象,推荐使用这种
    • BeanFactory 延迟加载,什么时候使用什么时候创建对象,适用于多例对象

【Spring】Spring 入门的更多相关文章

  1. spring快速入门(四)

    一.在spring快速入门(三)的基础上,我们来了解BeanFactory及配置. Client package com.murong.client; import org.springframewo ...

  2. spring快速入门(三)

    一.在spring快速入门(二)的基础上,原先我们是采用构造方法完成对象的注入.这里还有其他的方法可以完成注入,通过set方法来完成. 修改UserActionImpl package com.mur ...

  3. spring快速入门(二)

    一.在spring快速入门(一)的基础上,我们来了解spring是如何解决对象的创建以及对象之间的依赖关系的问题 (比如client中依赖UserAction的具体实现,UserActionImpl中 ...

  4. Spring快速入门

    什么是Spring Spring是分层的JavaSE/EE full-stack(一站式) 轻量级开源框架 分层 SUN提供的EE的三层结构:web层.业务层.数据访问层(持久层/集成层) Strut ...

  5. 161103、Spring Boot 入门

    Spring Boot 入门 spring Boot是Spring社区较新的一个项目.该项目的目的是帮助开发者更容易的创建基于Spring的应用程序和服务,让更多人的人更快的对Spring进行入门体验 ...

  6. Spring MVC 入门教程示例 (一)

    今天和大家分享下  Spring MVC  入门教程 首先还是从 HelloWorld  web 工程开始 -------------------------- 1.首先创建一个Maven Web工程 ...

  7. spring boot 入门操作(二)

    spring boot入门操作 使用FastJson解析json数据 pom dependencies里添加fastjson依赖 <dependency> <groupId>c ...

  8. spring boot 入门操作(三)

    spring boot入门操作 devtools热部署 pom dependencies里添加依赖 <dependency> <groupId>org.springframew ...

  9. Spring Boot入门教程1、使用Spring Boot构建第一个Web应用程序

    一.前言 什么是Spring Boot?Spring Boot就是一个让你使用Spring构建应用时减少配置的一个框架.约定优于配置,一定程度上提高了开发效率.https://zhuanlan.zhi ...

  10. Spring Boot入门教程2-1、使用Spring Boot+MyBatis访问数据库(CURD)注解版

    一.前言 什么是MyBatis?MyBatis是目前Java平台最为流行的ORM框架https://baike.baidu.com/item/MyBatis/2824918 本篇开发环境1.操作系统: ...

随机推荐

  1. Dell R740 使用U盘安装 CentOS7.4 出现Warning:dracut-initqueue timeout - starting timeout scripts解决办法

    使用使用UltraISO软碟通刻录U盘,然后在Dell R740服务器安装CentOS7.4会出现如下错误: 解决办法: 1.使用blkid确认U盘的盘符,截图如下: 2.按F11键重启 3.进入启动 ...

  2. 差分约束系统——POJ1275

    之前做过差分,但是没做过差分约束系统. 正好在学军机房听课讲到这道题,就顺带学了一下. 其实...就是列不等式组然后建图 作为蒟蒻,当然是不会加二分优化的啦...但是poj上还是94ms跑过了qwq ...

  3. 【学习笔记】浅析后缀自动机(SAM)及基础应用

    解决子串相关问题的强大工具 我们知道一个长度为 \(n\) 的字符串中所有的子串数目为 \(O(n^2)\) 个,这很大程度上限制了我们对某些子串相关问题的研究.所以有没有解决方案,使得我们可以在可承 ...

  4. 基于Fisco-Bcos的区块链智能合约-简单案例实践

    一.智能合约介绍 智能合约是指把合同/协议条款以代码的形式电子化地放到区块链网络上.FISCO BCOS平台支持两种智能合约类型:Solidity智能合约与预编译智能合约 Solidity与Java类 ...

  5. 第一章、Docker 简介

    笔记内容来自:第一本Docker书 [澳] James Turnbull 著 李兆海 刘斌 巨震 ​ Docker 是一个能够把开发的应用程序自动部署到容器的开源引擎.(由Docker 公司,前dot ...

  6. 廖雪峰官网学习js 数组

    indexOf( )    某字符的位置 slice 相当于string 的substring 切片 a = ['a','b',1,2,3] (5) ["a", "b&q ...

  7. 迭代 可迭代对象 迭代器的bj

    1.迭代的概念 迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果.每一次对过程的重复称为一次"迭代",而每一次迭代得到的结果会作为下一次迭代的初始值. 2.可迭代对象 ...

  8. Kubernetes弃用Docker后怎么办?

    本文转自Rancher Labs 近期,Kubernetes在其最新的Changelog中宣布,自Kubernetes 1.20之后将弃用Docker作为容器运行时.这一消息在云原生领域激起了不小的水 ...

  9. [日常摸鱼]bzoj1038[ZJOI2008]瞭望塔-半平面交

    这回好好用半平面交写一次- 看了cls当年写的代码看了好久大概看懂了-cls太强辣 #include<cstdio> #include<iostream> #include&l ...

  10. MySQL(二):快速理解MySQL数据库索引

    索引 基本概念:索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现. 数据结构 Tree 指的是 Balance Tree,也就是平衡树.平衡树是一颗查找树,并 ...