Spring基础入门

Spring

前言

Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。

Spring特点

  1. 解耦合,简化开发
  2. 支持AOP,面向切面编程
  3. 方便程序测试
  4. 方便和其他框架结合
  5. 方便进行事务操作
  6. 降低了API开发难度

IOC

概述

什么是IOC
  1. 控制反转,把对象的创建,赋值和对象之间的调用过程,交给spring管理
  2. 使用IOC的目的是为了降低耦合度
IOC底层原理
  1. xml解析、工厂模式、反射
  2. 通过xml解析配置文件,获取bean的id值,和对象所属类class,然后创建工厂类factory,利用class的全限定名称,使用反射技术创建类的对象
  3. IOC容器实现的两种方式:BeanFactory、ApplicationContext(BeanFactory的子类),前者在开始的时候只会加载xml配置,不会创建对象,后者会在加载xml配置的时候创建所有的对象
  4. java创建对象的方式有哪些:构造方法、反射、序列化、克隆
  5. IOC的实现举例:servlet对象——由tomcat根据web.xml中的配置,创建servlet对象,tomcat也称之为容器,可以叫servlet容器。除此之外tomcat中存放的还有listener,filter对象

DI的两种实现方式

  1. 在spring的配置文件中,使用标签和属性完成,叫做基于xml的di实现,也叫set设值注入
  2. 在spring中的注解,完成赋值,叫做基于注解的di实现
IOC基于xml配置文件的

DI:依赖注入(dependency injection)是ioc的技术实现,只需要在程序中提供要使用的对象唯一标识的别名就可以,至于对象如何在容器中创建,赋值,查找都由容器内部实现。其底层使用的反射机制。

spring的ioc实现步骤
  1. 创建maven项目
  2. 加入maven依赖,spring:5.2.5版本,Junit依赖
  3. 创建类(接口和他的实现类)和没有使用框架一样,就是普通的类
  4. 创建xml配置文件,声明类的信息,这些类由spring创建和管理
  5. 测试spring创建的类
xml注入代码实现
  1. set注入:设值注入,spring调用类的set方法,在set方法可以给属性赋值,80%都是使用的set注入

  2. 构造注入:spring调用类的有参数构造方法,创建对象,在构造方法中完成赋值

  3. set注入之简单数据类型赋值:

    <bean id="student" class="com.etekcity.domain.Student">
        <property name="name" value="alex"></property>
        <property name="name" value="alex">
        	<value><![CDATA[<<alexz>>]]></value>
        </property>
        <!-- 值包含特殊符号可以使用转义,也可以使用CDATA格式 -->
        <property name="age" value="20"></property>
        <property name="age">
        	<null/>
        </property>
        <!-- 设值空值 -->
    </bean>
    
  4. set注入之引用数据类型赋值

    <bean id="student" class="com.etekcity.domain.Student">
        <property name="name" value="alex"></property>
        <property name="age" value="20"></property>
        <property name="school" ref="school"></property>
    </bean>
    
    <bean id="school" class="com.etekcity.domain.School">
        <property name="sname" value="一高"></property>
        <property name="address" value="渝北"></property>
    </bean>
    
  5. 构造注入方式

    <bean id="student" class="com.etekcity.domain.Student">
        <constructor-arg name="name" value="tom"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="school" ref="school"></constructor-arg>
    </bean>
    
    <bean id="school" class="com.etekcity.domain.School">
        <property name="sname" value="一高"></property>
        <property name="address" value="渝北"></property>
    </bean>
    
  6. 集合注入方式

    <bean id="student" class="com.etekcity.domain.Student">
        <property name="name" value="alex"></property>
        <property name="age" value="20"></property>
        <property name="map">
            <map>
                <entry key="mapKey" value="mapValue"></value>
            </map>
        <property>
        <!-- 集合属性注入 -->
        <property name="school" ref="school"></property>
    </bean>
    
    <bean id="school" class="com.etekcity.domain.School">
        <property name="sname" value="一高"></property>
        <property name="address" value="渝北"></property>
    </bean>
    
  7. 自动注入:byName、byType

    <!--引用类型的自动注入,spring框架根据某些规则可以给引用类型赋值,不需要你再给引用类型赋值,使用的规则常用的是byName,byType-->
    <bean id="student" class="com.etekcity.domain.Student" autowire="byName">
        <property name="name" value="alex"></property>
        <property name="age" value="20"></property>
    <!--
        这里不用配置school引用类型, 通过byName自动注入,自动寻找bean的id和属性名称一样的bean
        byType,是指java类中引用类型的数据类型和spring配置文件<bean>的class属性值是同源关系,这样的bean能够赋值给引用类型
        同源:
        java类中引用类型的数据类型和spring配置文件<bean>的class属性值是一样的
        java类中引用类型的数据类型和spring配置文件<bean>的class属性值父子类关系
        java类中引用类型的数据类型和spring配置文件<bean>的class属性值接口和实现类关系
    -->
    </bean>
    
    <bean id="school" class="com.etekcity.domain.School">
        <property name="sname" value="一高"></property>
        <property name="address" value="渝北"></property>
    </bean>
    
  8. 使用外部配置文件,创建properties配置文件,以jdbc配置为例

    <context:property-placeholder location="classpath:spring.properties"/>
    <!-- xml中指定properties配置文件类路径 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.passwd}"></property>
        <property name="maxActive" value="${jdbc.maxActive}"></property>
    </bean>
    
多配置文件使用方式

使用多配置文件:项目比较大,多人协作开发时,单配置文件十分不方便

<!--
    使用多配置文件时,主配置文件中不定义对象bean
    语法: <import resource="path">
    也是在类路径下寻找,在classes目录下,可以使用通配符,使用通配符时要注意,不能匹配到主配置文件名字,且其他的配置必须和主配置放在同一目录下
-->
<import resource="classpath:student.xml"></import>
<import resource="classpath:student-*.xml"></import>
注解注入代码实现
xml配置文件和注解使用

使用注解的步骤:加入maven依赖,spring-context,在你加入spring-context的同时,间接加入了spring-aop的依赖,使用注解必须使用spring-aop依赖;在类中加入spring的注解(多个不同功能的注解);在spring的配置文件中,加入一个组件扫描器,说明注解在你的项目中的位置

<context:component-scan base-package="com.etekcity.domain"/>

Spring中提供了创建对象的注解有:@Component、@Repository、@Service、@Controller

  1. @Component:创建对象的,等同标签的功能,属性value就是对象的名称,也就是bean的id值,在spring容器中唯一
  2. @Repository:用在持久层类的上面,放在到实现类上面,表示创建dao对象,能访问数据库
  3. @Service:用在业务层类上面,放在service的实现类上,创建service对象,service对象是做业务处理的,可以有事务等功能的
  4. @Controller:用在控制器的上面:放在控制器类的上面,创建控制器对象能够接受用户提交的参数,显示请求的处理结果

以上三个注解的使用语法和component一样的,都能创建对象,但是这三个注解还有额外的功能:给项目分层,赋予不同的角色

package com.etekcity.domain;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

// 可以省略value,@Component("myStudent")
// 不指定对象名称,只用@Component,默认的对象名称是类名首字母小写
@Component(value = "myStudent")
public class Student {

    // 简单类型赋值,使用注解赋值
    @Value("alex")
    private String name;
    @Value("20")
    private Integer age;

    @Autowired
    private School school;
}

Spring中注入属性值的注解有:@Value、@Autowried、@Qualifiler、@Resource(jdk自带的自动注入注解)

// 简单类型赋值,使用注解赋值
@Value("alex")
private String name;
@Value("20")
private Integer age;
/*
	@Autowired,spring框架提供的注解,实现引用类型的赋值,spring中通过注解给引用类型赋值,使用的是自动注入的原理,支持byType,byName,@Autowired默认使用的是byType自动注入
*/
@Autowired
private School school;
/*
	@Resource,是jdk自带的自动注入注解,默认用的byName,先使用byName自动注入,如果赋值失败,会使用byType自动注入
*/
@Resource(name = "school")   // 只是用byName模式,需要指定值
private School school;
@Autowired
@Qualifiler(value="School1") // 根据名称注入,当接口有多个实现类时,使用注解表示使用哪个实现类对象
private School school;
使用纯注解开发模式

去掉xml配置文件:创建一个配置类,使用注解完成相关配置,示例如下:获取交给spring管理的bean

public class App {
    public static void main( String[] args ){
        // ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigClass.class);
        Student student = (Student) applicationContext.getBean("student");
        System.out.println(student);
    }
}

Bean作用域及生命周期

Spring中有两种类型的bean:普通类型的bean(xml中定义的类型和使用时返回的类型必须一致),工厂bean(实现FactoryBean接口,xml中定义的类型和使用时返回的类型可以不一致)

Spring中bean的作用域
  1. spring中默认使用的是单例(scope=“singleton”)模式,定义bean的时候可以通过属性scope=“protoType”,设置为多实例模式
  2. 当实例scope设置为多实例作用域时,并不会在加载配置的时候创建多个实例对象,会在每次调用getBean方法的时候创建
  3. 除此之外scope的值还有session,request,几乎不用
Spring中bean的生命周期
  • 通过构造器(默认使用无参构造器)创建对象
  • 对象赋值(依赖注入)
  • 对象的前置处理器(对象实现BeanPostProcessor接口,重写前置方法)
  • 初始化对象(需要配置初始化方法,通过属性设置 init-method=“对象的初始化方法”)
  • 对象的后置处理器(对象实现BeanPostProcessor接口,重写后置方法)
  • 使用对象
  • 销毁对象(需要配置销毁方法,通过属性设置 destroy-method=“对象的销毁方法”)

AOP

AOP概述

AOP底层是采用的动态代理模式实现的,代理模式又分为,jdk动态代理,cglib动态代理。面向切面编程,基于动态代理,是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。

怎么面向切面编程,其步骤是

  1. 需要分析项目功能时,找出切面
  2. 合理的安排切面的执行时间
  3. 合理的安全的切面执行过程,在哪个类那个方法增加增强功能

AOP的技术实现

Spring框架实现:spring在内部实现了aop规范,能做aop的工作,主要在事务处理时使用aop,我们项目开发中很少使用spring的aop实现,因为spring的aop比较笨重

AspectJ:一个开源的专门做aop的框架,spring框架中集成了aspectJ框架。AspectJ框架实现aop有两种方式:使用xml配置文件、使用注解,通常使用注解方式

面向切面编程术语
  1. joinpoint:连接点,类中那些方法可以被增强,这些方法称为连接点
  2. pointcut:切入点,类中实际被增强的方法
  3. 目标对象:被增强方法所属的类
  4. advice:通知,实际被增强的逻辑代码
  5. aspect:切面,是一个动作,指把通知应用到切入点的过程
AspectJ切入点表达式

用于指定切入点,表达式的原型是

  1. execution(modifiers-pattern?(方法的修饰符) res-type-pattern(方法的返回类型)declaring-type-pattern?(包名类名)name-pattern(param-pattern)(方法名(参数类型和参数个数)throws-pattern(方法抛出的异常))
  2. 切入点表达式中带问号的是可选的,可以没有,简写为:execution(访问权限修饰符 方法返回值类型 方法声明(参数列表) 异常类型)
  3. 切入点表达式要匹配的对象就是目标方法的方法名,所以execution表达式中明显就是方法的签名,注意,表达式中黑色文字表示可以省略部分,各部分间用空格分开,在其中可以使用以下符号
*	0至多个任意字符
..  用在方法参数中,表示任意多个参数,用在包名后,表示当前包及其子包路径
+   用在类名后,表示当前类及其子类,用在接口后,表示当前接口及其实现类
AspectJ框架中5个注解
  1. @Before,目标方法调用之前执行切面功能
  2. @AfterReturing,目标方法调用且返回结果后执行切面功能
  3. @Around,目标方法调用前和调用后,执行切面功能
  4. @AfterThrowing,目标方法调用后有异常时后执行,可以监控目标方法,有异常时切面功能可以是发送短信,邮件这些
  5. @After,最终通知,在目标方法调用之后执行,不管是否有异常,总是会执行的,一般做清除操作
  6. 如果目标类没有实现接口,spring会默认调用cglib动态代理,如果有接口,又想使用cglib,则可以在主配置文件中把proxy-target-class值设置为true
<aop:aspectj-autoproxy proxy-target-class="true"/>
AspectJ代码实现举例
  1. 创建接口及其实现类
  2. xml配置开启AspectJ自动生成代理对象
<aop:aspectj-autoproxy proxy-target-class="true"/>

使用完全注解,设置配置类

@Configuration
@ComponentScan(basePackages = {"com.etekcity.cloud.domain"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigClass {
	...
}

创建切面,使用注解配置

@Aspect  // @Aspect: 是aspect框架中的注解,表示当前类是切面类,定义在类的上面
public class BeforeAspect {
    /*
        定义方法,方法是实现切面功能的
        方法的定义要求:
        1.公共方法public
        2.方法没有返回值
        3.方法名称自定义
        4.方法可以有参数,也可以没有参数,如果有参数,有几个参数类型可以使用, JoinPoint

        @Before(value="切入点表达式")
        
        增强功能方法的参数:
        JoinPoint:指实际被增强的业务方法
        作用:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参
        如果你的切面功能中需要用到方法的信息,就加入joinpoint,这个joinpoint参数的值是由框架赋予,
        必须是第一个位置的参数
     */
    @Before(value = "execution(public void *..SomeServiceImpl.doSome(..))")
    public void beforeAspect(JoinPoint joinPoint){
        System.out.println("方法的签名:" + joinPoint.getSignature());
        System.out.println("方法的名称:" + joinPoint.getSignature().getName());
        Object[] args = joinPoint.getArgs();
        for(Object arg : args){
            System.out.println(arg);
        }
        System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:" + new Date());
    }
}

多个相同切点的抽取,减少重复的切入点表达式,定义一个切入点方法,加上@Pointcut注解

/*
@Pointcut: 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的,可以用@Pointcut
属性:value,切入点表达式
*/
@Pointcut(value = "execution(public String *..SomeServiceImpl.doOther(..))")
private void pt(){}

// 使用
@After(value = "pt()")
public void afterAdvice(){
    // 增强功能代码
}

多个增强类对同一个业务方法进行增强,设置增强类的优先级,在增强类上面设置优先级,使用注解@Order(数字类型值,数字越小,优先级越高)

@Aspect
@Order(1)
public class BeforeAspect {
    ...
}

JdbcTemplate

说明

  1. 了解spring自带的JdbcTemplate,操作数据库的方式就可以了,实际使用的都是框架,mybatis
  2. xml文件中配置DataSource对象,JdbcTemplate对象
  3. 创建Dao接口和实现类,实现类中注入JdbcTemplate对象,操作数据库,增删改查
  4. 创建Service类,注入Dao对象,调用Dao对象具体方法
  5. 批量操作:batchxxx,传入的集合参数

事务

事务概述

  1. 什么是事务:事务是指一组sql语句的集合操作,要么都执行成功,要么都执行失败,事务具有原子性
  2. 什么时候使用事务:当操作涉及到多个表或者是多个sql语句的操作,需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的

事务特性

  1. A,原子性:最小单元,不可再分割
  2. C,一致性:一个事务执行时,要么同时成功,要么同时失败
  3. I,隔离性:不同事务是相互隔离的,互不影响
  4. D,持久性:事务一旦结束(commit提交,rollback),不可以再回滚

Spring处理事务的模型,使用的步骤都是固定的,把事务使用的信息提供给Spring就可以了

事务管理器

  1. 事务管理器可以代替你完成commit,rollback,事务管理器是一个接口和他的众多实现类,接口:PlatformTransactionManager,定义了事务重要方法commit,rollback,实现类:Spring把每一种数据库访问技术对应的事务处理类都创建好了
  2. mybatis访问数据库—DataSourceTransactionManager
  3. hibernate访问数据库—HibernateTransactionManager
  4. 事务提交事务,回滚事务的时机:当业务方法执行成功,没有异常时,事务管理器commit、当业务方法抛出运行时异常,事务管理器rollback、当业务方法抛出非运行时异常,主要是受检异常,提交事务commit

Spring中事务技术实现

Spring中事务的技术实现有两种,分为编程式和声明式,编程式是自己写代码实现事务,一般不用,通常都是使用声明式,声明式由可以分为使用xm配置,和注解两种方式

注解方式
  1. 创建事务管理器对象
  2. 开启事务注解驱动,告诉Spring框架,我要使用注解的方式管理事务
  3. Spring给业务加入事务:在你的业务方法执行之前,先开启事务,在业务方法之后提交事务或者回滚事务,使用的是AspectJ的环绕通知
  4. 在方法上面加入注解@Transactional,这个注解也可以加载类上面,表示类中所有方法都使用事务
<!--使用spring的事务管理-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!--开启事务注解驱动,告诉Spring使用注解管理事务,创建代理对象-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    /*
        给buy方法增加事务的功能,@Transactional注解,buy方法要是public的
        一下的注解参数是默认的,默认抛出运行时异常,回滚事务, @Transactional
        如果抛出的异常类型在rollbackFor中,一定会回滚,不区分是否是运行一样
     */
    @Transactional(
    propagation = Propagation.REQUIRED, // 传播行为,必须要事务
    isolation = Isolation.DEFAULT, // 隔离级别
    readOnly = false, // 只读
    rollbackFor = {NullPointerException.class, NotEnoughException.class} // 表示发生指定的异常时一定回滚
    )
@Transactional注解参数说明
  1. propagation:事务的传播行为,是针对多事务方法的调用,Spring中有7种事务传播行为,常用的是propagation_required、propagation_supports、propagation_requires_new
  2. isolation:事务隔离级别,有4个值
  3. timeout:事务的超时时间:表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚,单位是秒,整数值,默认是-1
  4. readOnly:是否只读,默认false
  5. rollbackFor:出现了哪些异常时进行回滚
  6. noRollbackFor:出现了哪些异常时不进行回滚
xml配置方式

xml配置文件方式,适合大型项目,有很多的类,方法,需要大量的配置事务,使用AspectJ框架功能,在spring配置文件中声明类,方法需要的事务,这种方式使业务和事务完全分离,实现步骤为:

  1. 加入依赖
  2. 声明事务管理器对象
  3. 配置通知
  4. 配置切入点和切面
<!--1.使用AspectJ框架的事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--2.声明业务方法的事务属性-->
    <tx:advice id="advice" transaction-manager="transactionManager">
        <!--事务属性-->
        <tx:attributes>
            <!--给具体的方法配置事务,name:完整的方法名称,不带有饱和类,方法可以使用通配符,*表示任意字符-->
            <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="com.etekcity.excep.NotEnoughException,java.lang.NullPointerException"
                       read-only="false"/>
        </tx:attributes>
    </tx:advice>

    <!--上面配置的方法,没有指定那个包下的方法,如果需要指定包和类需要配置如下-->
    <aop:config>
        <!--
            id:切入点表达式名称,唯一
            expression:切入点表达式
        -->
        <aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))"/>
        <!--关联方法-->
        <aop:advisor advice-ref="advice" pointcut-ref="servicePointcut"></aop:advisor>
    </aop:config>
使用全注解模式,设置配置类
@Configuration
@ComponentScan(basePackages = {"com.etekcity.cloud.domain"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 开启AspectJ框架,使用AOP
@EnableTransactionManagement // 开启事务
public class ConfigClass {

    @Bean
    public DruidDataSource druidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://user");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");

        return druidDataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){

        return new JdbcTemplate(dataSource);
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

}

Spring 5新特性

概述

  1. Spring 5框架最低支持Java 8 版本,运行时兼容Java 9

  2. Spring 5框架自带了通用的日志,移除了log4j,采用了新版的log4j2

  3. Spring 5框架核心容器支持@Nullable注解

    • 用在属性,表示属性值可以为空
    • 用在方法,表示方法返回值可以为空
    • 用在方法参数,表示方法参数值可以为空
  4. Spring 5框架核心容器支持函数式风格(也就是lambda表达式)

  5. JUnit 5单元测试框架

    @RunWith(SpringJUnit4ClassRunner.class) // junit 4
    @ContextConfiguration(classes = ConfigClass.class)
    
    @ExtendWith(SpringExtension.class) // junit 5
    @SpringJUnitConfig(locations = "xxx.xml")
    public class JUnitTest {
    }
    

Spring 5框架新功能

SpringWebflux介绍

  1. SpringWebflux是Spring 5新添加的模块,用于web开发的,功能和SpringMVC类似的,Webflux 使用的是当前一种比较流行的响应式编程框架
  2. 传统web框架,比如SpringMVC,这些基于Servlet容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在Servlet 3.1以后才支持,核心是基于Reactor的相关API实现的。
什么是异步非阻塞
  1. 异步和同步针对调用者:调用发送请求,如果等待对方回应之后才继续做其他事情,称之为同步,反之,如果发送请求后不等待对方响应就去做其他事情,称之为异步
  2. 阻塞和非阻塞针对被调用者:被调用着收到请求之后,如果被调用者执行完请求任务才给出响应,称之为阻塞,反之,如果被调用者接收到请求后,立即给出响应,然后才执行请求任务,称之为非阻塞
Webflux特点
  1. 第一是非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
  2. 第二是函数式编程:Spring 5 框架基于Java 8,Webflux 使用Java8函数式编程方式实现路由请求

响应式编程

  1. 响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
  2. 响应式编程用到的是观察者设计模式,用到的两个类是:Observer、Observable
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值