SpringCloud的阿里巴巴相关开源组件

 

Sentinel

阿里巴巴开源产品,把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Nacos

阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

RocketMQ

Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流计算平台。

Dubbo

Apache Dubbo™ 是一款高性能 Java RPC 框架。

Seata

阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

 

参考:https://github.com/alibaba/spring-cloud-alibaba

 

SpringCloud框架

最近一直在针对SpringCloud框架做项目,从中踩了不少的坑,也渐渐梳理出了一些内容,由于SpringCloud作为一个全家桶,其中东西太多,所以这时候就要有所取舍,这里就想把自己比较常用组件及架构推荐上来。本文基于SpringBoot 1.5.7和SpirngCloud  Dalston.SR5。

 

 

 

针对这个架构图我分层介绍一下:

1、是web服务器的选型,这个我选择的是nginx+keepalived,haproxy也是一个选择,但是haproxy在反向代理处理跨域访问的时候问题很多。所以我们nginx有些地方做了keep-alive模式处理,减少了三次握手的次数,提高了连接效率。keepalived做nginx的负载,虚拟一个vip对外,两个nginx做高可用,nginx本身反向代理zuul集群。

2、api gateway,这里的zuul很多人诟病,说是速度慢推荐直接用nginx,这里我还是推荐使用zuul的,毕竟zuul含有拦截器和反向代理,在权限管理、单点登录、用户认证时候还是很有用的,而且zuul自带ribbon负载均衡,如果你直接用nginx,还需要单独做一个feign或者ribbon层,用来做业务集群的负载层,毕竟直接把接口暴露给web服务器太危险了。这里zuul带有ribbon负载均衡和hystrix断路器,直接反向代理serviceId就可以代理整个集群了。

3、业务集群,这一层我有些项目是分两层的,就是上面加了一个负载层,下面是从service开始的,底层只是单纯的接口,controller是单独一层由feign实现,然后内部不同业务服务接口互调,直接调用controller层,只能说效果一般,多了一次tcp连接。所以我推荐合并起来,因为做过spring cloud项目的都知道,feign是含有ribbon的,而zuul也含有ribbon,这样的话zuul调用服务集群,和服务集群间接口的互调都是高可用的,保证了通讯的稳定性。Hystrix还是要有的,没有断路器很难实现服务降级,会出现大量请求发送到不可用的节点。当然service是可以改造的,如果改造成rpc方式,那服务之间互调又是另外一种情况了,那就要做成负载池和接口服务池的形式了,负载池调用接口池,接口池互相rpc调用,feign client只是通过实现接口达到了仿rpc的形式,不过速度表现还是不错的。

4、redis缓存池,这个用来做session共享,分布式系统session共享是一个大问题。同时呢,redis做二级缓存对降低整个服务的响应时间,并且减少数据库的访问次数是很有帮助的。当然redis cluster还是redis sentinel自己选择。

5、eurake注册中心这个高可用集群,这里有很多细节,比如多久刷新列表一次,多久监测心跳什么的,都很重要。

6、spring admin,这个是很推荐的,这个功能很强大,可以集成turbine断路器监控器,而且可以定义所有类的log等级,不用单独去配置,还可以查看本地log日志文件,监控不同服务的机器参数及性能,非常强大。它加上elk动态日志收集系统,对于项目运维非常方便。

7、zipkin,这个有两种方式,直接用它自己的功能界面查看方式,或者用stream流的方式,由elk动态日志系统收集。但是我必须要说,这个对系统的性能损害非常大,因为链路追踪的时候会造成响应等待,而且等待时间非常长接近1秒,这在生产环境是不能忍受的,所以生产环境最好关掉,有问题调试的时候再打开。

8、消息队列,这个必须的,分布式系统不可能所有场景都满足强一致性,这里只能由消息队列来作为缓冲,这里我用的是Kafka。

9、分布式事物,我认为这是分布式最困难的,因为不同的业务集群都对应自己的数据库,互相数据库不是互通的,互相服务调用只能是相互接口,有些甚至是异地的,这样造成的结果就是网络延迟造成的请求等待,网络抖动造成的数据丢失,这些都是很可怕的问题,所以必须要处理分布式事物。我推荐的是利用消息队列,采取二阶段提交协议配合事物补偿机制,具体的实现需要结合业务,这里篇幅有限就不展开说了。

10、config配置中心,这是很有必要的,因为服务太多配置文件太多,没有这个很难运维。这个一般利用消息队列建立一个spring cloud bus,由git存储配置文件,利用bus总线动态更新配置文件信息。

11、实时分布式日志系统,logstash收集本地的log文件流,传输给elasticsearch,logstash有两种方式,1、是每一台机器启动一个logstash服务,读取本地的日志文件,生成流传给elasticsearch。2、logback引入logstash包,然后直接生产json流传给一个中心的logstash服务器,它再传给elasticsearch。elasticsearch再将流传给kibana,动态查看日志,甚至zipkin的流也可以直接传给elasticsearch。这个配合spring admin,一个查看动态日志,一个查看本地日志,同时还能远程管理不同类的日志级别,对集成和运维非常有利。

最后要说说,spring cloud的很多东西都比较精确,比如断路器触发时间、事物补偿时间、http响应时间等,这些都需要好好的设计,而且可以优化的点非常多。比如:http通讯可以使用okhttp,jvm优化,nio模式,数据连接池等等,都可以很大的提高性能。

还有一个docker问题,很多人说不用docker就不算微服务。其实我个人意见,spring cloud本身就是微服务的,只需要jdk环境即可。编写dockerfile也无非是集成jdk、添加jar包、执行jar而已,或者用docker compose,将多个不同服务的image组合run成容器而已。但是带来的问题很多,比如通讯问题、服务器性能损耗问题、容器进程崩溃问题,当然如果你有一套成熟的基于k8s的容器管理平台,这个是没问题的,如果没有可能就要斟酌了。而spring cloud本身就是微服务分布式的架构,所以个人还是推荐直接机器部署的,当然好的DevOps工具将会方便很多。

 

服务注册发现组件Eureka工作原理

图片描述

服务网关组件Zuul工作原理

图片描述

跨域时序图

图片描述

Eureka与Ribbon整合工作原理

图片描述

解决分布式一致性

图片描述

级联故障流程

图片描述

断路器组件Hystrix工作原理

图片描述

分布式追踪Sleuth工作原理

图片描述

SpringBoot自动配置工作原理

图片描述

 

转自http://www.imooc.com/article/23679

转自:https://blog.csdn.net/cyc3552637/article/details/80254490

dubbo同步调用、异步调用和是否返回结果源码分析和实例

0. dubbo同步调用、异步调用和是否返回结果配置

(1)dubbo默认为同步调用,并且有返回结果。

(2)dubbo异步调用配置,设置 async=”true”,异步调用可以提高效率。

(3)dubbo默认是有返回结果,不需要返回,可以设置return=”false”,不需要返回值,可以减少等待结果时间。

 

1. 源码分析(dubbo版本:2.6.0)

dubbo自身底层调用是使用netty异步实现的,默认同步调用返回结果,是通过获取ResponseFuture,然后使用ReentrantLock的await使当前线程等待结果,设置返回的。下面是部分核心代码部分:

com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(Invocation)

 

    @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
        inv.setAttachment(Constants.VERSION_KEY, version);

        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            // 获取是否异步配置async
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            // 获取是否需要返回结果配置return
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            // 获取超时配置timeout
            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
            if (isOneway) {
                // 不管是否异步,只要不需要返回结果,直接异步调用,设置结果为null
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
                // 如果异步,并且需要返回结果,调用后设置结果future
                ResponseFuture future = currentClient.request(inv, timeout);
                RpcContext.getContext().setFuture(new FutureAdapter(future));
                return new RpcResult();
            } else {
                // 如果同步,并且需要返回结果,调用后在此等待,直到有结果设置结果,或者超时抛出异常。
                RpcContext.getContext().setFuture(null);
                return (Result) currentClient.request(inv, timeout).get();
            }
        } catch (TimeoutException e) {
            throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        } catch (RemotingException e) {
            throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

2. 实例

这里使用dubbo源码自带的例子

(1)在dubbo-demo-api中定义异步服务

package com.alibaba.dubbo.demo;

public interface AsyncDemoService {
    String sayHello(String name);
}

(2)在dubbo-demo-provider中实现

package com.alibaba.dubbo.demo.provider;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.alibaba.dubbo.demo.AsyncDemoService;
import com.alibaba.dubbo.rpc.RpcContext;

public class AsyncDemoServiceImpl implements AsyncDemoService{

    @Override
    public String sayHello(String name) {
        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return "Hello " + name + ", response form provider: " + RpcContext.getContext().getLocalAddress();
    }

}

(3)在dubbo-demo-provider.xml中配置服务信息

    class="com.alibaba.dubbo.demo.provider.AsyncDemoServiceImpl"/>


    interface="com.alibaba.dubbo.demo.AsyncDemoService" ref="asyncDemoService"/>

(4)在dubbo-demo-consumer.xml中配置调用服务信息,设置为异步调用async=”true” 

    interface="com.alibaba.dubbo.demo.AsyncDemoService">
        
    

(5)在dubbo-demo-consumer的Consumer类中增加调用

        AsyncDemoService asyncDemoService = (AsyncDemoService) context.getBean("asyncDemoService");
        asyncDemoService.sayHello("world");
        // 通过 1.源码分析 中可以知道异步返回结果放到了RpcContext.getContext()中
        Future future =  RpcContext.getContext().getFuture();

      try {
          String hello = future.get(1, TimeUnit.SECONDS);
          System.out.println(hello);
        } catch (InterruptedException e) {
          e.printStackTrace();
        } catch (ExecutionException e) {
          e.printStackTrace();
        } catch (TimeoutException e) {
          e.printStackTrace();
        }

(6)不需要返回结果,配置dubbo-demo-consumer.xml 中return=”false”,同步异步的调用方式一样的,很简单,只需要调用一下就可以继续其他操作,下面是异步的例子。

        AsyncDemoService asyncDemoService = (AsyncDemoService) context.getBean("asyncDemoService");
        asyncDemoService.sayHello("world");

 

dubbo框架设计学习

1.整体设计

(1)架构图

/dev-guide/images/dubbo-framework.jpg

图例说明:

  • 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
  • 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
  • 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
  • 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。

各层说明:

  • config 配置层:对外配置接口,以 ServiceConfigReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy为中心,扩展接口为 ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactoryRegistryRegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 ClusterDirectoryRouterLoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactoryMonitorMonitorService
  • protocol 远程调用层:封装 RPC 调用,以 InvocationResult 为中心,扩展接口为 ProtocolInvokerExporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 RequestResponse 为中心,扩展接口为 ExchangerExchangeChannelExchangeClientExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 ChannelTransporterClientServerCodec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 SerializationObjectInputObjectOutputThreadPool

 

(2)功能成熟度(部分可用于生产环节的功能,使用时注意版本是否支持)

Feature

Maturity

Strength

Problem

Advise

User

分组聚合

Tested

分组聚合返回值,用于菜单聚合等服务

特殊场景使用

可用于生产环境

 

泛化引用

Stable

泛化调用,无需业务接口类进行远程调用,用于测试平台,开放网关桥接等

 

可用于生产环境

Alibaba

泛化实现

Stable

泛化实现,无需业务接口类实现任意接口,用于Mock平台

 

可用于生产环境

Alibaba

隐式传参

Stable

附加参数

 

可用于生产环境

 

本地存根

Stable

在客户端执行部分逻辑

 

可用于生产环境

Alibaba

本地伪装

Stable

伪造返回结果,可在失败时执行,或直接执行,用于服务降级

需注册中心支持

可用于生产环境

Alibaba

延迟暴露

Stable

延迟暴露服务,用于等待应用加载warmup数据,或等待spring加载完成

 

可用于生产环境

Alibaba

 (3)策略成熟度部分可用于生产环节的策略,使用时注意版本是否支持)

 协议:

Feature

Maturity

Strength

Problem

Advise

User

Dubbo协议

Stable

采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)

在大文件传输时,单一连接会成为瓶颈

可用于生产环境

Alibaba

Rmi协议

Stable

可与原生RMI互操作,基于TCP协议

偶尔会连接失败,需重建Stub

可用于生产环境

Alibaba

Hessian协议

Stable

可与原生Hessian互操作,基于HTTP协议

需hessian.jar支持,http短连接的开销大

可用于生产环境

 

 

 序列化:

Feature

Maturity

Strength

Problem

Advise

User

Hessian Serialization

Stable

性能较好,多语言支持(推荐使用)

Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码

可用于生产环境

Alibaba

Java Serialization

Stable

Java原生支持

性能较差

可用于生产环境

 

 

集群容错:

Feature

Maturity

Strength

Problem

Advise

User

Failover Cluster

Stable

失败自动切换,当出现失败,重试其它服务器,通常用于读操作(推荐使用)

重试会带来更长延迟

可用于生产环境

Alibaba

Failfast Cluster

Stable

快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作

如果有机器正在重启,可能会出现调用失败

可用于生产环境

Alibaba

Failsafe Cluster

Stable

失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作

调用信息丢失

可用于生产环境

Monitor

Failback Cluster

Tested

失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作

不可靠,重启丢失

可用于生产环境

Registry

Forking Cluster

Tested

并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作

需要浪费更多服务资源

可用于生产环境

 

Broadcast Cluster

Tested

广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于更新提供方本地状态

速度慢,任意一台报错则报错

可用于生产环境

 

 

负载均衡

Feature

Maturity

Strength

Problem

Advise

User

Random LoadBalance

Stable

随机,按权重设置随机概率(推荐使用)

在一个截面上碰撞的概率高,重试时,可能出现瞬间压力不均

可用于生产环境

Alibaba

RoundRobin LoadBalance

Stable

轮询,按公约后的权重设置轮询比率

存在慢的机器累积请求问题,极端情况可能产生雪崩

可用于生产环境

 

LeastActive LoadBalance

Stable

最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少请求

不支持权重,在容量规划时,不能通过权重把压力导向一台机器压测容量

可用于生产环境

 

ConsistentHash LoadBalance

Stable

一致性Hash,相同参数的请求总是发到同一提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动

压力分摊不均

可用于生产环境

 

 

2.依赖关系

/dev-guide/images/dubbo-relation.jpg

图例说明:

  • 图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层或模块,蓝色的表示与业务有交互,绿色的表示只对 Dubbo 内部交互。
  • 图中背景方块 Consumer, Provider, Registry, Monitor 代表部署逻辑拓扑节点。
  • 图中蓝色虚线为初始化时调用,红色虚线为运行时异步调用,红色实线为运行时同步调用。
  • 图中只包含 RPC 的层,不包含 Remoting 的层,Remoting 整体都隐含在 Protocol 中。

3.调用链

 4.dubbo源码学习

源码学习,内容比较多,可以参考http://dubbo.apache.org/zh-cn/docs/user/quick-start.html 中的“源码导读”部分,代码中加了很多注释,讲解很详细,配合源码debug学习,效果更好。

 

参考:http://dubbo.apache.org/zh-cn/docs/user/quick-start.html

 

springcloud分布式事务Atomikos实例

0.JTA(Java Transaction Manager)的介绍

(1)jta与jdbc

简单的说 jta是多库的事务 jdbc是单库的事务

(2)XA与JTA

XA : XA是一个规范或是一个事务的协议.XA协议由Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准.

XA规范定义了:
1. TransactionManager : 这个TransactionManager可以通过管理多个ResourceManager来管理多个Resouce,也就是管理多个数据源
2. XAResource : 针对数据资源封装的一个接口
3. 两段式提交 : 多数据源事务提交的机制

JTA(Java Transaction Manager) : 是Java规范,是XA在Java上的实现.
1. TransactionManager : 常用方法,可以开启,回滚,获取事务. begin(),rollback()…
2. XAResouce : 资源管理,通过Session来进行事务管理,commit(xid)…
3. XID : 每一个事务都分配一个特定的XID

JTA是如何实现多数据源的事务管理呢?

主要的原理是两阶段提交,以上面的请求业务为例,当整个业务完成了之后只是第一阶段提交,在第二阶段提交之前会检查其他所有事务是否已经提交,如果前面出现了错误或是没有提交,那么第二阶段就不会提交,而是直接rollback操作,这样所有的事务都会做Rollback操作.

(3)jta特点

JTA的有点就是能够支持多数据库事务同时事务管理,满足分布式系统中的数据的一致性.但是也有对应的弊端:

  1. 两阶段提交
  2. 事务时间太长,锁数据太长
  3. 低性能,低吞吐量

1.maven的pom中增加spring-boot-starter-jta-atomikos

2.配置jta和atomikos的多数据源,例如:

  jta:
    enabled: true
    atomikos:
      datasource:
        order:
          xa-properties.url: jdbc:h2:mem:dborder
          xa-properties.user: sa
          xa-properties.password:
          xa-data-source-class-name: org.h2.jdbcx.JdbcDataSource
          unique-resource-name: order
          max-pool-size: 10
          min-pool-size: 1
          max-lifetime: 10000
          borrow-connection-timeout: 10000
        log:
          xa-properties.url: jdbc:h2:mem:dblog
          xa-properties.user: sa
          xa-properties.password:
          xa-data-source-class-name: org.h2.jdbcx.JdbcDataSource
          unique-resource-name: log
          max-pool-size: 10
          min-pool-size: 1
          max-lifetime: 10000
          borrow-connection-timeout: 10000

3.atomikos的jta与jpa的配置和数据源配置

import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;

import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

/**
 * Created by caibosi on 2018-07-25.
 */
public class AtomikosJtaPlatform extends AbstractJtaPlatform {

    private static TransactionManager transactionManager;

    private static UserTransaction userTransaction;

    public static void setTransactionManager(TransactionManager transactionManager) {
        AtomikosJtaPlatform.transactionManager = transactionManager;
    }

    public static void setUserTransaction(UserTransaction userTransaction) {
        AtomikosJtaPlatform.userTransaction = userTransaction;
    }

    @Override
    protected TransactionManager locateTransactionManager() {
        return transactionManager;
    }

    @Override
    protected UserTransaction locateUserTransaction() {
        return userTransaction;
    }
}

4.使用jta 在方法上面使用@Transactional

    @Transactional
    public void newOrderRollback(String userId,String productCode,int quantity){
        UserOrder userOrder = new UserOrder();
        userOrder.setUserId(userId);
        userOrder.setProductCode(productCode);
        userOrder.setQuantity(quantity);
        userOrderDao.save(userOrder);

        EventLog eventLog = new EventLog();
        eventLog.setOperation("new order");
        eventLog.setOperator(userId);
        eventLogDao.save(eventLog);

        throw new RuntimeException("test jta rollback");
    }

 

5.在调用newOrderRollback时会回滚

参考:https://github.com/SpringCloud/spring-cloud-code.git ch24

  https://www.jianshu.com/p/3938e7172443

spring事务的三种配置应用实例

0.项目结构

具体代码见:https://github.com/xkzhangsan/spring-transaction-practice.git,包括创建表sql在内。

1.编程式事务使用DataSourceTransactionManager git对应版本: v0.0.1

(1)Spring的xml配置:spring\app-context.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <description>Example configuration to get you started.description>

    <context:component-scan base-package="com.xkzhangsan.tx" />
    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver">property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/xkspringmvc">property>
        <property name="user" value="xktest">property>
        <property name="password" value="123456">property>
    bean>
    
    <bean id="accountDao" class="com.xkzhangsan.tx.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource">property>
    bean>
     
    <bean id="accountService" class="com.xkzhangsan.tx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao">property>
    bean>
    
    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource">property>
    bean>    
     

beans>

(2)AccountServiceImpl

package com.xkzhangsan.tx.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.xkzhangsan.tx.dao.AccountDao;
import com.xkzhangsan.tx.service.AccountService;

public class AccountServiceImpl implements AccountService{
     
    private AccountDao accountDao;
    
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void transfer(String outer, String inner, int money) {
        DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
        transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
        TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
        try {
            // 数据库操作
            accountDao.out(outer, money);
            int i = 1/0;
            accountDao.in(inner, money);
             
            dataSourceTransactionManager.commit(status);// 提交
        } catch (Exception e) {
            dataSourceTransactionManager.rollback(status);// 回滚
        }
    }
 
}

 

2.声明式事务基于AOP的xml配置 git对应版本: v0.0.2

(1)Spring的xml配置:spring\app-context.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <description>Example configuration to get you started.description>

    <context:component-scan base-package="com.xkzhangsan.tx" />
    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver">property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/xkspringmvc">property>
        <property name="user" value="xktest">property>
        <property name="password" value="123456">property>
    bean>
    
    <bean id="accountDao" class="com.xkzhangsan.tx.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource">property>
    bean>
     
    <bean id="accountService" class="com.xkzhangsan.tx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao">property>
    bean>
    
    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource">property>
    bean>
     
    
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
        tx:attributes>
    tx:advice>
     
    
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.xkzhangsan.tx.service..*.*(..))"/>
    aop:config>    
     

beans>

(2)AccountServiceImpl

package com.xkzhangsan.tx.service.impl;

import com.xkzhangsan.tx.dao.AccountDao;
import com.xkzhangsan.tx.service.AccountService;

public class AccountServiceImpl implements AccountService{
     
    private AccountDao accountDao;
     
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void transfer(String outer, String inner, int money) {
        accountDao.out(outer, money);
        int i=10/0;
        accountDao.in(inner, money);
    }
 
}

 

3. 声明式事务基于AOP的注解 git对应版本: v0.0.3

(1)Spring的xml配置:spring\app-context.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <description>Example configuration to get you started.description>

    <context:component-scan base-package="com.xkzhangsan.tx" />
    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver">property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/xkspringmvc">property>
        <property name="user" value="xktest">property>
        <property name="password" value="123456">property>
    bean>
    
    <bean id="accountDao" class="com.xkzhangsan.tx.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource">property>
    bean>
     
    <bean id="accountService" class="com.xkzhangsan.tx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao">property>
    bean>
    
    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource">property>
    bean>
    
    <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>    
     

beans>

(2)AccountServiceImpl

package com.xkzhangsan.tx.service.impl;

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.xkzhangsan.tx.dao.AccountDao;
import com.xkzhangsan.tx.service.AccountService;

public class AccountServiceImpl implements AccountService{
     
    private AccountDao accountDao;
     
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    @Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)
    public void transfer(String outer, String inner, int money) {
        accountDao.out(outer, money);
        int i=10/0;
        accountDao.in(inner, money);
    }
 
}

 

参考:https://www.cnblogs.com/ysocean/p/7617620.html

spring事务概念与获取事务时事务传播行为源码分析

一、事务状态:org.springframework.transaction.TransactionStatus

isNewTransaction 是否是新事务

hasSavepoint 是否有保存点

setRollbackOnly 设置回滚

isRollbackOnly 是否回滚

flush 刷新

isCompleted 是否完成

二、传播行为

1.传播行为分类

类别

简称

无事务

已有事务

PROPAGATION_REQUIRED

Required(必须-默认)

创建一个新的事务

使用该事务

PROPAGATION_SUPPORTS

Supports(支持)

以非事务执行

使用该事务

PROPAGATION_MANDATORY

Mandatory(强制)

抛异常

使用该事务

PROPAGATION_REQUIRES_NEW

requires_new(必须新的)

创建一个新的事务

原事务挂起,创建一个新的事务

PROPAGATION_NOT_SUPPORTED

not_supported(不支持)

以非事务执行

原事务挂起,以非事务执行

PROPAGATION_NEVER

Never(从不)

以非事务执行

抛异常

PROPAGATION_NESTED

Nested(嵌套)

创建一个新事务

在嵌套事务内执行

2.传播行为源码分析

(1)获取事务org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(TransactionDefinition definition)

    //---------------------------------------------------------------------
    // Implementation of PlatformTransactionManager
    //---------------------------------------------------------------------

    /**
     * This implementation handles propagation behavior. Delegates to
     * doGetTransaction, isExistingTransaction
     * and doBegin.
     * @see #doGetTransaction
     * @see #isExistingTransaction
     * @see #doBegin
     */
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();

        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();

        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }
     // 判断是否存在事务
        if (isExistingTransaction(transaction)) {
       // 如果有事务,根据传播行为处理
// Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); }       // Check definition settings for new transaction. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); }      // 下面是不存在事务情况下,根据传播行为处理
     // 如果配置的是mandatory强制,抛异常 
// No existing transaction found -> check propagation behavior to find out how to proceed. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); }
     // 如果是required必须、requires_new必须新的和nested嵌套时,创建一个新的事物
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }

 

(2)已有事务时处理org.springframework.transaction.support.AbstractPlatformTransactionManager.TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)

    /**
     * Create a TransactionStatus for an existing transaction.
     */
    private TransactionStatus handleExistingTransaction(
            TransactionDefinition definition, Object transaction, boolean debugEnabled)
            throws TransactionException {
     // 下面是对已有事务,根据传播行为处理
    
     // 如果配置的是never从不,抛异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'"); }      // 如果配置的是not_support不支持,原事务挂起,以非事务执行 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } Object suspendedResources = suspend(transaction); boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); }      // 如果配置的是requires_new,原事务挂起,创建一个新事务执行 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]"); } SuspendedResourcesHolder suspendedResources = suspend(transaction); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException beginEx) { resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } catch (Error beginErr) { resumeAfterBeginException(transaction, suspendedResources, beginErr); throw beginErr; } }      // 如果配置是nested嵌套,在嵌套事务中执行 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException( "Transaction manager does not allow nested transactions by default - " + "specify 'nestedTransactionAllowed' property with value 'true'"); } if (debugEnabled) { logger.debug("Creating nested transaction with name [" + definition.getName() + "]"); } if (useSavepointForNestedTransaction()) { // Create savepoint within existing Spring-managed transaction, // through the SavepointManager API implemented by TransactionStatus. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; } else { // Nested transaction through nested begin and commit/rollback calls. // Usually only for JTA: Spring synchronization might get activated here // in case of a pre-existing JTA transaction. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, null); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } } // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. if (debugEnabled) { logger.debug("Participating in existing transaction"); } if (isValidateExistingTransaction()) { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) { Constants isoConstants = DefaultTransactionDefinition.constants; throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (currentIsolationLevel != null ? isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) : "(unknown)")); } } if (!definition.isReadOnly()) { if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is"); } } } boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); }

 

三、 Spring隔离级别:

  (1)、ISOLATION_DEFAULT:使用后端数据库默认的隔离级别

  (2)、ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读

  (3)、ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

  (4)、ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生

  (5)、ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

部分参考:https://www.cnblogs.com/ysocean/p/7617620.html

spring根据beanName获取bean

spring根据beanName获取bean主要实现:

org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(String, Class, Object[], boolean)

    @SuppressWarnings("unchecked")
    protected  T doGetBean(
            final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
        // 转换对应的beanName
        final String beanName = transformedBeanName(name);
        Object bean;
        
        // 直接尝试从缓存获取
        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // 原型模式循环依赖直接抛出异常(默认只有单例情况下才会尝试解决循环依赖问题)
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
            
            // 检查这个工厂中是否存在bean定义
            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }

            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }
            
            // 将存储xml配置文件的GernericBeanDefinition转换为RootBeanDefinition,如果指定的BeanName是子Bean的话同时会合并父类的相关属性
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);
            
            // 保证当前bean所依赖的bean的初始化(递归处理)
            // Guarantee initialization of beans that the current bean depends on.
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dependsOnBean : dependsOn) {
                    getBean(dependsOnBean);
                    registerDependentBean(dependsOnBean, beanName);
                }
            }
            
            // bean的实例化
            // Create bean instance.
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, new ObjectFactory() {
                    public Object getObject() throws BeansException {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, new ObjectFactory() {
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; " +
                            "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        // 检查需要的类型是否符合bean的实际类型,如果不是进行类型转换
        // Check if required type matches the type of the actual bean instance.
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            catch (TypeMismatchException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to convert bean '" + name + "' to required type [" +
                            ClassUtils.getQualifiedName(requiredType) + "]", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        // 返回bean
        return (T) bean;
    }

 

实际获取过程非常复杂,上面只是显示了获取的主要流程。

参考:spring源码深度解析

spring容器的功能扩展

容器的扩展功能主要实现为:

org.springframework.context.support.AbstractApplicationContext.refresh()

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 准备刷新的上下文环境
            // Prepare this context for refreshing.
            prepareRefresh();
            
            // 初始化BeanFactory,并进行xml文件读取
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 对BeanFactory进行各种功能填充
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // 子类覆盖方法做额外的处理
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // 激活各种BeanFactory处理器
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // 注册拦截bean创建的bean处理器,这里只是注册,真正的调用时在getBean时候
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // 为上下文初始化Message源,即不同语言的消息体,国际化处理
                // Initialize message source for this context.
                initMessageSource();

                // 初始化应用消息广播器,并放入ApplicationEventMulticaster中
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // 留给子类来初始化其他的bean
                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // 在所有注册的bean中查找listener bean,注册到消息广播器中
                // Check for listener beans and register them.
                registerListeners();

                // 初始化剩下的单实例(非延迟加载的)
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

 

 

参考:spring源码深度解析

 

机甲大师S1机器人编程学习,Windows 10 安装Scratch和简单实例学习

 

 

机甲大师S1支持 Scratch 3.0以上版本。

Scratch官方网址:https://scratch.mit.edu/  最新版本为3.4.0

今天在Windows 10上,安装最新版本。

1.下载Scratch,二种方式:

(1)直接使用官网下载链接:https://downloads.scratch.mit.edu/desktop/Scratch%20Desktop%20Setup%203.4.0.exe  在迅雷下载比较快。具体点击“+”,新建任务,输入前面下载地址即可。

(2)官网页面下载:

打开官网:https://scratch.mit.edu/

看最下方的 “Offline Editor”,点击,如图:

下载页面,点击download,如图:需要win10系统才能用。

 

2.安装

(1)双击运行,如图:点击“谢谢,不用了”。

(2)初始界面:

 

3.简单实例

(1)添加初始事件,如图:

(2)添加运动,如图:

(3)点击绿色旗帜运行,结果如图:

 

 4.保存文件

(1)如图

(2)如图,填写文件名称,点击保存。

 

写多了java、js、shell等语言,用一下Scratch,感觉挺有意思的!