在上篇讨论里我们提到了become/unbecome。由于它们本质上是堆栈操作,所以只能在较少的状态切换下才能保证堆栈操作的协调及维持程序的清晰逻辑。对于比较复杂的程序流程,Akka提供了FSM:一种通过状态变化进行功能切换的Actor。FSM模式的状态转变特别适合对应现实情况中的程序流程,我们可以用每一种状态来代表一个程序流程。FSM是个trait,定义如下:

trait FSM[S, D] extends Actor with Listeners with ActorLogging {...}

我们看到:FSM就是一个特殊的Actor。带着两个类型参数:S代表状态类型,D代表状态数据类型。实际上S和D结合起来就是FSM的内部状态,即:SomeState+DataX 和 SomeState+DataY分别代表不同的Actor内部状态,这点从State定义可以得到信息:

/**
* This captures all of the managed state of the [[akka.actor.FSM]]: the state
* name, the state data, possibly custom timeout, stop reason and replies
* accumulated while processing the last message.
*/
case class State[S, D](stateName: S, stateData: D, timeout: Option[FiniteDuration] = None, stopReason: Option[Reason] = None, replies: List[Any] = Nil) {...}

我们可以用下面的表达式来代表FSM功能:

State(SA) x Event(E) -> Actions (A), State(SB)

意思是:假如在状态SA发生了事件E,那么FSM应该实施操作A并把状态转换到SB。这里面操作Action代表某项功能,事件Event是个新的类型,定义如下:

/**
* All messages sent to the [[akka.actor.FSM]] will be wrapped inside an
* `Event`, which allows pattern matching to extract both state and data.
*/
final case class Event[D](event: Any, stateData: D) extends NoSerializationVerificationNeeded

Event[D]是个包嵌消息和数据的类型。理论上FSM是通过接收Event来确定运行功能和下一个状态转换,就像普通的Actor接收Message原理一样。我们用上一篇里的FillSeasons作为这次FSM示范的例子。首先定义State,Data:

trait Seasons   //States
case object Spring extends Seasons
case object Summer extends Seasons
case object Fall extends Seasons
case object Winter extends Seasons class SeasonInfo(talks: Int, month: Int) //Data
case object BeginSeason extends SeasonInfo(,)

四个状态分别是:春夏秋冬。SeasonInfo代表数据类型,含被问候次数talks,季中月份month两个参数,每季含1,2,3三个月份。目前我们只支持两种功能消息:

object FillSeasons {
sealed trait Messages //功能消息
case object HowYouFeel extends Messages
case object NextMonth extends Messages
}

这种普通Actor的消息类型对应到FSM的Event类型中的event:Any。也就是说FSM在收到功能消息后需要构建一个Event类型实例并把消息包嵌在里面。因为FSM继承了Actor,所以它必须实现receive函数。下面是FSM.receive的源代码:

 /*
* *******************************************
* Main actor receive() method
* *******************************************
*/
override def receive: Receive = {
case TimeoutMarker(gen) ⇒
if (generation == gen) {
processMsg(StateTimeout, "state timeout")
}
case t @ Timer(name, msg, repeat, gen) ⇒
if ((timers contains name) && (timers(name).generation == gen)) {
if (timeoutFuture.isDefined) {
timeoutFuture.get.cancel()
timeoutFuture = None
}
generation +=
if (!repeat) {
timers -= name
}
processMsg(msg, t)
}
case SubscribeTransitionCallBack(actorRef) ⇒
// TODO Use context.watch(actor) and receive Terminated(actor) to clean up list
listeners.add(actorRef)
// send current state back as reference point
actorRef ! CurrentState(self, currentState.stateName)
case Listen(actorRef) ⇒
// TODO Use context.watch(actor) and receive Terminated(actor) to clean up list
listeners.add(actorRef)
// send current state back as reference point
actorRef ! CurrentState(self, currentState.stateName)
case UnsubscribeTransitionCallBack(actorRef) ⇒
listeners.remove(actorRef)
case Deafen(actorRef) ⇒
listeners.remove(actorRef)
case value ⇒ {
if (timeoutFuture.isDefined) {
timeoutFuture.get.cancel()
timeoutFuture = None
}
generation +=
processMsg(value, sender())
}

除timer,subscription等特殊功能外,case value => ... 就是处理自定义消息的地方了。我们看到FSM是用processMsg(value, sender())来处理消息的。processMsg又调用了processEvent:

  private def processMsg(value: Any, source: AnyRef): Unit = {
val event = Event(value, currentState.stateData)
processEvent(event, source)
} private[akka] def processEvent(event: Event, source: AnyRef): Unit = {
val stateFunc = stateFunctions(currentState.stateName)
val nextState = if (stateFunc isDefinedAt event) {
stateFunc(event)
} else {
// handleEventDefault ensures that this is always defined
handleEvent(event)
}
applyState(nextState)
}

在processEvent里的stateFunction是个Map,以stateName为主键存放StateFunction:

 /*
* State definitions
*/
private val stateFunctions = mutable.Map[S, StateFunction]()

而StateFuction是:

 type StateFunction = scala.PartialFunction[Event, State]

FSM的receive函数在收到消息后把消息包嵌入新构建的Event然后在processEvent里通过stateName取出相应的StateFunction后传入Event产生新的状态State。用户提供的StateFunction是通过FSM的when函数压进stateFunction Map里的:

  /**
* Insert a new StateFunction at the end of the processing chain for the
* given state. If the stateTimeout parameter is set, entering this state
* without a differing explicit timeout setting will trigger a StateTimeout
* event; the same is true when using #stay.
*
* @param stateName designator for the state
* @param stateTimeout default state timeout for this state
* @param stateFunction partial function describing response to input
*/
final def when(stateName: S, stateTimeout: FiniteDuration = null)(stateFunction: StateFunction): Unit =
register(stateName, stateFunction, Option(stateTimeout)) private def register(name: S, function: StateFunction, timeout: Timeout): Unit = {
if (stateFunctions contains name) {
stateFunctions(name) = stateFunctions(name) orElse function
stateTimeouts(name) = timeout orElse stateTimeouts(name)
} else {
stateFunctions(name) = function
stateTimeouts(name) = timeout
}
}

我们看到when调用了register在stateFunction Map中按stateName放置StateFunction。FSM的这个stateFunction Map解决了become/unbecome产生的堆栈问题。FSM有个比较规范的结构,拿上面例子的FeelingSeasons结构做个示范:

class FillSeasons extends FSM[Seasons,SeasonInfo] with ActorLogging {
import FillSeasons._
startWith(Spring,SeasonInfo(,)) //起始状态
when(Spring) { //状态在春季
case Event(HowYouFeel,seasonInfo) => ...
}
when(Summer) { //夏季状态
case Event(HowYouFeel,_) =>
}
when(Fall) { //秋季状态
case Event(HowYouFeel,SeasonInfo(tks,mnth)) =>
}
when(Winter) { //冬季状态
case Event(HowYouFeel,si@ SeasonInfo(tks,_)) =>
}
whenUnhandled { //所有状态未处理的Event
case Event(NextMonth,seasonInfo) =>
}
onTransition {
case Spring -> Summer => log.info("Season changed from Spring to Summer month 1")
case Summer -> Fall => log.info("Season changed from Summer to Fall month 1")
case Fall -> Winter => log.info("Season changed from Fall to Winter month 1")
case Winter -> Spring => log.info("Season changed from Winter to Spring month 1")
} initialize() //设定起始状态
}

基本上是按照各状态定义事件处理函数StateFunction的。也可以包括状态转换处理函数TransitionHandler:

  type TransitionHandler = PartialFunction[(S, S), Unit]

最后,initialize()确定起始状态是否安排正确:

  /**
* Verify existence of initial state and setup timers. This should be the
* last call within the constructor, or [[akka.actor.Actor#preStart]] and
* [[akka.actor.Actor#postRestart]]
*
* An initial `currentState -> currentState` notification will be triggered by calling this method.
*
* @see [[#startWith]]
*/
final def initialize(): Unit =
if (currentState != null) makeTransition(currentState)
else throw new IllegalStateException("You must call `startWith` before calling `initialize`")

完整的FSM FeelingSeasons定义如下:

class FillSeasons extends FSM[Seasons,SeasonInfo] with ActorLogging {
import FillSeasons._
startWith(Spring,SeasonInfo(,)) //起始状态
when(Spring) { //状态在春季
case Event(HowYouFeel,seasonInfo) =>
val numtalks = seasonInfo.talks +
log.info(s"It's ${stateName.toString}, feel so gooood! You've asked me ${numtalks}times.")
stay using seasonInfo.copy(talks = numtalks)
}
when(Summer) { //夏季状态
case Event(HowYouFeel,_) =>
val numtalks = stateData.talks +
log.info(s"It's ${stateName.toString}, it's so hot! You've asked me ${numtalks}times")
stay().using(stateData.copy(talks = numtalks))
}
when(Fall) { //秋季状态
case Event(HowYouFeel,SeasonInfo(tks,mnth)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's no so bad. You've asked me ${numtalks}times.")
stay using SeasonInfo(numtalks,mnth)
}
when(Winter) { //冬季状态
case Event(HowYouFeel,si@ SeasonInfo(tks,_)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's freezing cold! You've asked me ${numtalks}times.")
stay using si.copy(talks = numtalks)
}
whenUnhandled { //所有状态未处理的Event
case Event(NextMonth,seasonInfo) =>
val mth = seasonInfo.month
if (mth <= ) {
log.info(s"It's month ${mth+1} of ${stateName.toString}")
stay using seasonInfo.copy(month = mth + )
}
else {
goto(nextSeason(stateName)) using SeasonInfo(,)
}
}
onTransition {
case Spring -> Summer => log.info(s"Season changed from Spring to Summer month ${nextStateData.month}")
case Summer -> Fall => log.info(s"Season changed from Summer to Fall month ${nextStateData.month}")
case Fall -> Winter => log.info(s"Season changed from Fall to Winter month ${nextStateData.month}")
case Winter -> Spring => log.info(s"Season changed from Winter to Spring month ${nextStateData.month}")
} initialize() //设定起始状态
log.info(s"It's month 1 of ${stateName.toString}") //季节转换顺序
def nextSeason(season: Seasons): Seasons =
season match {
case Spring => Summer
case Summer => Fall
case Fall => Winter
case Winter => Spring
}
}

首先注意StateFunction中SeasonInfo的各种意思同等的表达方式及nextStateData。FSM状态数据用不可变对象(immutable object)最安全,所以在更新时必须用case class 的copy或直接构建新的SeasonInfo实例。

我们再来看看processEvent的作业流程:

 private[akka] def processEvent(event: Event, source: AnyRef): Unit = {
val stateFunc = stateFunctions(currentState.stateName)
val nextState = if (stateFunc isDefinedAt event) {
stateFunc(event)
} else {
// handleEventDefault ensures that this is always defined
handleEvent(event)
}
applyState(nextState)
}

先运算用户定义的StateFunction处理事件Event获取新的状态State。然后调用applyState运算makeTransition处理状态转换(currentState = nextState):

  private[akka] def applyState(nextState: State): Unit = {
nextState.stopReason match {
case None ⇒ makeTransition(nextState)
case _ ⇒
nextState.replies.reverse foreach { r ⇒ sender() ! r }
terminate(nextState)
context.stop(self)
}
} private[akka] def makeTransition(nextState: State): Unit = {
if (!stateFunctions.contains(nextState.stateName)) {
terminate(stay withStopReason Failure("Next state %s does not exist".format(nextState.stateName)))
} else {
nextState.replies.reverse foreach { r ⇒ sender() ! r }
if (currentState.stateName != nextState.stateName || nextState.notifies) {
this.nextState = nextState
handleTransition(currentState.stateName, nextState.stateName)
gossip(Transition(self, currentState.stateName, nextState.stateName))
this.nextState = null
}
currentState = nextState def scheduleTimeout(d: FiniteDuration): Some[Cancellable] = {
import context.dispatcher
Some(context.system.scheduler.scheduleOnce(d, self, TimeoutMarker(generation)))
} currentState.timeout match {
case SomeMaxFiniteDuration ⇒ // effectively disable stateTimeout
case Some(d: FiniteDuration) if d.length >= ⇒ timeoutFuture = scheduleTimeout(d)
case _ ⇒
val timeout = stateTimeouts(currentState.stateName)
if (timeout.isDefined) timeoutFuture = scheduleTimeout(timeout.get)
}
}
}

我们用FSM DSL的stay, goto,using来取得新的FSM状态和数据:

 /**
* Produce transition to other state.
* Return this from a state function in order to effect the transition.
*
* This method always triggers transition events, even for `A -> A` transitions.
* If you want to stay in the same state without triggering an state transition event use [[#stay]] instead.
*
* @param nextStateName state designator for the next state
* @return state transition descriptor
*/
final def goto(nextStateName: S): State = FSM.State(nextStateName, currentState.stateData) /**
* Produce "empty" transition descriptor.
* Return this from a state function when no state change is to be effected.
*
* No transition event will be triggered by [[#stay]].
* If you want to trigger an event like `S -> S` for `onTransition` to handle use `goto` instead.
*
* @return descriptor for staying in current state
*/
final def stay(): State = goto(currentState.stateName).withNotification(false) // cannot directly use currentState because of the timeout field

stay,goto返回结果都是State[S,D]类型。using是State类型的一个方法:

   /**
* Modify state transition descriptor with new state data. The data will be
* set when transitioning to the new state.
*/
def using(@deprecatedName('nextStateDate) nextStateData: D): State[S, D] = {
copy(stateData = nextStateData)
}

我们看到using的主要作用是把当前状态数据替换成新状态的数据。

Akka的FSM是一个功能强大的Actor类型,所以配备了一套完整的DSL来方便FSM编程。FSM的DSL语句包括:

  final def startWith(stateName: S, stateData: D, timeout: Timeout = None): Unit =
currentState = FSM.State(stateName, stateData, timeout) final def goto(nextStateName: S): State = FSM.State(nextStateName, currentState.stateData) final def stay(): State = goto(currentState.stateName).withNotification(false) final def stop(): State = stop(Normal) final def transform(func: StateFunction): TransformHelper = new TransformHelper(func) ...

State[S,D]也提供了一些比较实用的方法函数:

case class State[S, D](stateName: S, stateData: D, timeout: Option[FiniteDuration] = None, stopReason: Option[Reason] = None, replies: List[Any] = Nil) {
... // defined here to be able to override it in SilentState
def copy(stateName: S = stateName, stateData: D = stateData, timeout: Option[FiniteDuration] = timeout, stopReason: Option[Reason] = stopReason, replies: List[Any] = replies): State[S, D] = {
new State(stateName, stateData, timeout, stopReason, replies)
} /**
* Modify state transition descriptor to include a state timeout for the
* next state. This timeout overrides any default timeout set for the next
* state.
*
* Use Duration.Inf to deactivate an existing timeout.
*/
def forMax(timeout: Duration): State[S, D] = timeout match {
case f: FiniteDuration ⇒ copy(timeout = Some(f))
case Duration.Inf ⇒ copy(timeout = SomeMaxFiniteDuration) // we map the Infinite duration to a special marker,
case _ ⇒ copy(timeout = None) // that means "cancel stateTimeout". This marker is needed
} // so we do not have to break source/binary compat.
// TODO: Can be removed once we can break State#timeout signature to `Option[Duration]` /**
* Send reply to sender of the current message, if available.
*
* @return this state transition descriptor
*/
def replying(replyValue: Any): State[S, D] = {
copy(replies = replyValue :: replies)
} /**
* Modify state transition descriptor with new state data. The data will be
* set when transitioning to the new state.
*/
def using(@deprecatedName('nextStateDate) nextStateData: D): State[S, D] = {
copy(stateData = nextStateData)
} ...
}

FSM DSL中的transform是这样定义的:

  final class TransformHelper(func: StateFunction) {
def using(andThen: PartialFunction[State, State]): StateFunction =
func andThen (andThen orElse { case x ⇒ x })
} final def transform(func: StateFunction): TransformHelper = new TransformHelper(func)

我们看到TransformHelper用using对入参func:StateFunction施用用户提供的andThen: PartialFunction[State,State]后返回新的状态State。这个using与State.using是不同的。下面是一个transform用法例子:

when(Running) {
transform {
case Event(m, Target(Actor.noSender)) =>
goto(Uninitialised) using NoConfig case Event(m, Target(ref)) =>
ref ! m
stay
} using targetTransformer
} def targetTransformer: PartialFunction[State, State] = {
case s @ State(stateName, Target(ref), _, _, _) if ref.path.name.startsWith("testActor") =>
log.debug("Setting target to dead letters")
s.using(Target(Actor.noSender))
}

transform{...}产生的State传给了targetTransformer然后经过模式匹配拆分出properties后用s.using更新stateData。

与become/unbecome相同,我们也可以在FSM里使用stashing。下面是一个用例:

when(Uninitialised) {
case Event(Config(ref), _) =>
goto(Running) using Target(ref) case Event(_, _) =>
stash
stay
} when(Running) {
case Event(m, Target(ref)) =>
ref ! m
stay
} onTransition {
case Uninitialised -> Running => unstashAll()
}

当然,还有如stop,setTimer,replying,forMax,onTermination等方法和函数,这里就不一一详述了,有兴趣可以直接查询Akka/actor/FSM.scala。

下面是本次讨论的示范源码:

import akka.actor._

sealed trait Seasons   //States
case object Spring extends Seasons
case object Summer extends Seasons
case object Fall extends Seasons
case object Winter extends Seasons //sealed trait SeasonData //Data
case class SeasonInfo(talks: Int, month: Int) object FillSeasons {
sealed trait Messages //功能消息
case object HowYouFeel extends Messages
case object NextMonth extends Messages def props = Props(new FillSeasons)
} class FillSeasons extends FSM[Seasons,SeasonInfo] with ActorLogging {
import FillSeasons._
startWith(Spring,SeasonInfo(,)) //起始状态
when(Spring) { //状态在春季
case Event(HowYouFeel,seasonInfo) =>
val numtalks = seasonInfo.talks +
log.info(s"It's ${stateName.toString}, feel so gooood! You've asked me ${numtalks} times.")
stay using seasonInfo.copy(talks = numtalks)
}
when(Summer) { //夏季状态
case Event(HowYouFeel,_) =>
val numtalks = stateData.talks +
log.info(s"It's ${stateName.toString}, it's so hot! You've asked me ${numtalks} times")
stay().using(stateData.copy(talks = numtalks))
}
when(Fall) { //秋季状态
case Event(HowYouFeel,SeasonInfo(tks,mnth)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's no so bad. You've asked me ${numtalks} times.")
stay using SeasonInfo(numtalks,mnth)
}
when(Winter) { //冬季状态
case Event(HowYouFeel,si@ SeasonInfo(tks,_)) =>
val numtalks = tks +
log.info(s"It's ${stateName.toString}, it's freezing cold! You've asked me ${numtalks} times.")
stay using si.copy(talks = numtalks)
}
whenUnhandled { //所有状态未处理的Event
case Event(NextMonth,seasonInfo) =>
val mth = seasonInfo.month
if (mth <= ) {
log.info(s"It's month ${mth+1} of ${stateName.toString}")
stay using seasonInfo.copy(month = mth + )
}
else {
goto(nextSeason(stateName)) using SeasonInfo(,)
}
}
onTransition {
case Spring -> Summer => log.info(s"Season changed from Spring to Summer month ${nextStateData.month}")
case Summer -> Fall => log.info(s"Season changed from Summer to Fall month ${nextStateData.month}")
case Fall -> Winter => log.info(s"Season changed from Fall to Winter month ${nextStateData.month}")
case Winter -> Spring => log.info(s"Season changed from Winter to Spring month ${nextStateData.month}")
} initialize() //设定起始状态
log.info(s"It's month 1 of ${stateName.toString}") //季节转换顺序
def nextSeason(season: Seasons): Seasons =
season match {
case Spring => Summer
case Summer => Fall
case Fall => Winter
case Winter => Spring
}
} object FSMDemo extends App {
import scala.util.Random
val fsmSystem = ActorSystem("fsmSystem")
val fsmActor = fsmSystem.actorOf(FillSeasons.props,"fsmActor") ( to ).foreach { _ =>
( to Random.nextInt()).foreach{ _ =>
fsmActor ! FillSeasons.HowYouFeel
}
fsmActor ! FillSeasons.NextMonth
} scala.io.StdIn.readLine()
fsmSystem.terminate()
}

Akka(7): FSM:通过状态变化来转换运算行为的更多相关文章

  1. robotframework中的用evaluate关键字进行运算(随机数+转换+运算)

    当我们在写rf测试用例时,可能需要随机产生一些数据,可能需要将已有的数据进行转换,做简单的运算等:此时我们可以用万能的evaluate来实现 ,后面一般均适用python表达式来进行实现. 接下来详细 ...

  2. Akka FSM 源代码分析

    Akka FSM 源代码分析 萧猛 <simonxiao@qq.com> 啰嗦几句 有限状态机本身不是啥新奇东西,在GoF的设计模式一书中就有状态模式, 也给出了实现的建议.各种语言对状态 ...

  3. Akka(6): become/unbecome:运算行为切换

    通过一段时间的学习了解,加深了一些对Akka的认识,特别是对于Akka在实际编程中的用途方面.我的想法,或者我希望利用Akka来达到的目的是这样的:作为传统方式编程的老兵,我们已经习惯了直线流程方式一 ...

  4. Akka源码分析-FSM

    akka还有一个不常使用.但我觉得比较方便的一个模块,那就是FSM(有限状态机).我们知道了akka中Actor模型的具体实现之后,就会发现,Akka的actor可以非常方便的实现FSM.其实在akk ...

  5. Akka(16): 持久化模式:PersistentFSM-可以自动修复的状态机器

    前面我们讨论过FSM,一种专门为维护内部状态而设计的Actor,它的特点是一套特殊的DSL能很方便地进行状态转换.FSM的状态转换模式特别适合对应现实中的业务流程,因为它那套DSL可以更形象的描述业务 ...

  6. [翻译]AKKA笔记 - 有限状态机 -1

    原文地址:http://rerun.me/2016/05/21/akka-notes-finite-state-machines-1/ 我最近有个机会在工作上使用了Akka FSM,是个非常有趣的例子 ...

  7. Akka Typed 官方文档之随手记

    ️ 引言 近两年,一直在折腾用FP与OO共存的编程语言Scala,采取以函数式编程为主的方式,结合TDD和BDD的手段,采用Domain Driven Design的方法学,去构造DDDD应用(Dom ...

  8. SQL Server 进制转换函数

    一.背景 前段时间群里的朋友问了一个问题:“在查询时增加一个递增序列,如:0x00000001,即每一个都是36进位(0—9,A--Z),0x0000000Z后面将是0x00000010,生成一个像下 ...

  9. .NET LINQ 转换数据类型

    转换数据类型      转换方法更改输入对象的类型.      LINQ 查询中的转换运算可用于各种应用程序.下面是一些示例: Enumerable.AsEnumerable<TSource&g ...

随机推荐

  1. xmlplus 组件设计系列之二 - 按钮

    除了图标以外,按钮也许是最简单的组件了,现在来看看如何定义按钮组件. 使用原生按钮组件 在 xmlplus 中,HTML 元素也以组件的方式存在.所以,你可以直接通过使用 button 标签或者 in ...

  2. 日期时间插件flatpickr.js使用方法

    今天写代码时需要用一款插件来实现对input输入时间的格式控制,找到了两款功能合适而且比较美观的插件:基于Bootstrap的DateTimePicker.js和flatpickr.js插件.一开始先 ...

  3. ELK菜鸟手记 (三) - X-Pack权限控制之给Kibana加上登录控制以及index_not_found_exception问题解决

    0. 背景 我们在使用ELK进行日志记录的时候,通过网址在Kibana中查看我们的应用程序(eg: Java Web)记录的日志, 但是默认是任何客户端都可以访问Kibana的, 这样就会造成很不安全 ...

  4. 【国家集训队2012】tree(伍一鸣)

    Description 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一:  + u v c:将u到v的路径上的点的权值都加上自然数c:  - u1 v1 u2 ...

  5. IO和socket编程

    五一假期结束了,突然想到3周前去上班的路上看到槐花开的正好.放假也没能采些做槐花糕,到下周肯定就老了.一年就开一次的东西,比如牡丹,花期也就一周.而花开之时,玫瑰和月季无法与之相比.明日黄花蝶也愁.想 ...

  6. ListView在异步加载动态图片时,往往最后一项或几项被遮盖(IM场景居多)

    如果ListView中得默认图片比较小,新图片加载后,撑大ListView中的对应项,导致最后一项或几项被覆盖. 解决思路: 1.默认图片设定和新图大小一样,换句话说,新图加载后转成和默认图片一样的大 ...

  7. Java发布一个简单 webservice应用 并发送SOAP请求

    一.创建并发布一个简单的webservice应用 1.webservice 代码: package com.ls.demo; import javax.jws.WebMethod; import ja ...

  8. [刷题]Codeforces 785D - Anton and School - 2

    Description As you probably know, Anton goes to school. One of the school subjects that Anton studie ...

  9. 回答集编程背景(Answer Set Programming)

    毕业设计跟的导师是研究计算机理论的,花了三个月学习符号逻辑,试图优化一个回答集程序的求解器(Answer set solver).比起眼花缭乱的前端框架和热闹的社区讨论,符号逻辑就是一个挺小众的数学领 ...

  10. SQLServer树形数据结构的数据进行数据统计

    前言 前几天朋友问我,关于SQLServer数据库中对树形结构的表数据统计问题,需求大致如下: 分类表(递归数据),A的子分类是B,B的子分类是C--分类关系不间断,A为第一层,B为第二层,C为第三层 ...