cola架构:有限状态机(FSM)源码分析

目录

0. cola状态机简述

1.cola状态机使用实例

2.cola状态机源码解析

2.1 语义模型源码

2.1.1 Condition和Action接口

2.1.2 State

2.1.3 Transition接口

2.1.4 StateMachine接口

2.2 Builder模式

2.2.1 StateMachine Builder模式

2.2.2 ExternalTransitionBuilder---转移构造Builder模式

2.2.2.1 链式调用顺序保障

2.2.2.2 链式调用具体实现


0. cola状态机简述

cola状态机采用无状态设计,不存储中间状态,重点关注状态之间的转移,这样的设计使得cola更加的简单、轻量、易于上手,在高并发多线程场景下应用单例模式可以实现更高的性能;

cola状态机框架根据实际使用场景进行抽象领域建模,抽象出如下的语义模型:

  • State:状态
  • Event:事件,状态由事件触发,引起变化
  • Transition:流转,表示从一个状态到另一个状态
  • External Transition:外部流转,两个不同状态之间的流转
  • Internal Transition:内部流转,同一个状态之间的流转
  • Condition:条件,表示是否允许到达某个状态
  • Action:动作,到达某个状态之后,可以做什么
  • StateMachine:状态机

上述语义模型之间的实体关系模型如下:

  •  一个状态机实例(StateMachine)可以包含多个状态(State)
  • 每个状态可以包含多个状态转移(Transition)
  • 每个状态转移(Transition)包含初始状态、目标状态、驱动事件(Event),以及状态转移条件(Condition)和命中转移条件之后的动作(Action)

理清上述语义模型的概念后,下面通过源码分析来探究cola状态机的内部实现; 

1.cola状态机使用实例

在深入源码解析之前,首先通过一个具体的状态机实例了解状态机的使用方法,后面再逐步探究其执行过程:

public class StateMachineTest {

    static String MACHINE_ID = "TestStateMachine";

    static enum States {
        STATE1,
        STATE2,
        STATE3,
        STATE4
    }

    static enum Events {
        EVENT1,
        EVENT2,
        EVENT3,
        EVENT4,
        INTERNAL_EVENT
    }

    static class Context {
        String operator = "frank";
        String entityId = "123465";
    }

    @Test
    public void testExternalNormal() {
        StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
        builder.externalTransition()
            .from(States.STATE1)
            .to(States.STATE2)
            .on(Events.EVENT1)
            .when(checkCondition())
            .perform(doAction());

        StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
        States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());
        Assert.assertEquals(States.STATE2, target);
    }

    private Condition<Context> checkCondition() {
        return new Condition<Context>() {
            @Override
            public boolean isSatisfied(Context context) {
                System.out.println("Check condition : " + context);
                return true;
            }
        };
    }

    private Action<States, Events, Context> doAction() {
        return (from, to, event, ctx) -> {
            System.out.println(
                ctx.operator + " is operating " + ctx.entityId + " from:" + from + " to:" + to + " on:" + event);
        };
    }
}

 上面首先通过Builder模式创建StateMachine实例,其中定义了STATE1到STATE2的转移,然后通过fireEvent触发状态转移,在满足Condition时,执行了具体的动作Action;

2.cola状态机源码解析

cola状态机的核心逻辑都是围绕着上述的语义模型展开的,下面首先看下语义模型的源码;

2.1 语义模型源码

2.1.1 Condition和Action接口

 Condition接口:

/**
 * Condition
 *
 * @author Frank Zhang
 * @date 2020-02-07 2:50 PM
 */
public interface Condition<C> {

    /**
     * @param context context object
     * @return whether the context satisfied current condition
     */
    boolean isSatisfied(C context);

    default String name(){
        return this.getClass().getSimpleName();
    }
}

Action接口: 

/**
 * Generic strategy interface used by a state machine to respond
 * events by executing an {@code Action} with a {@link StateContext}.
 *
 * @author Frank Zhang
 * @date 2020-02-07 2:51 PM
 */
public interface Action<S, E, C> {

//    /**
//     * Execute action with a {@link StateContext}.
//     *
//     * @param context the state context
//     */
//    void execute(StateContext<S, E> context);

    public void execute(S from, S to, E event, C context);

}

Condition接口定义了isSatisfied方法评估是否命中条件;

Action接口通过方法execute执行具体的动作;

2.1.2 State

State接口定义如下:

/**
 * State
 *
 * @param <S> the type of state
 * @param <E> the type of event
 *
 * @author Frank Zhang
 * @date 2020-02-07 2:12 PM
 */
public interface State<S,E,C> extends Visitable{

    /**
     * Gets the state identifier.
     *
     * @return the state identifiers
     */
    S getId();

    /**
     * Add transition to the state
     * @param event the event of the Transition
     * @param target the target of the transition
     * @return
     */
    Transition<S,E,C> addTransition(E event, State<S, E, C> target, TransitionType transitionType);

    List<Transition<S,E,C>> getEventTransitions(E event);

    Collection<Transition<S,E,C>> getAllTransitions();

}

State接口实现:

/**
 * StateImpl
 *
 * @author Frank Zhang
 * @date 2020-02-07 11:19 PM
 */
public class StateImpl<S,E,C> implements State<S,E,C> {
    protected final S stateId;
    private EventTransitions eventTransitions = new EventTransitions();

    StateImpl(S stateId){
        this.stateId = stateId;
    }

    @Override
    public Transition<S, E, C> addTransition(E event, State<S,E,C> target, TransitionType transitionType) {
        Transition<S, E, C> newTransition = new TransitionImpl<>();
        newTransition.setSource(this);
        newTransition.setTarget(target);
        newTransition.setEvent(event);
        newTransition.setType(transitionType);

        Debugger.debug("Begin to add new transition: "+ newTransition);

        eventTransitions.put(event, newTransition);
        return newTransition;
    }

    @Override
    public List<Transition<S, E, C>> getEventTransitions(E event) {
        return eventTransitions.get(event);
    }

    @Override
    public Collection<Transition<S, E, C>> getAllTransitions() {
        return eventTransitions.allTransitions();
    }

    @Override
    public S getId() {
        return stateId;
    }

    @Override
    public String accept(Visitor visitor) {
        String entry = visitor.visitOnEntry(this);
        String exit = visitor.visitOnExit(this);
        return entry + exit;
    }

    @Override
    public boolean equals(Object anObject){
        if(anObject instanceof State){
            State other = (State)anObject;
            if(this.stateId.equals(other.getId()))
                return true;
        }
        return false;
    }

    @Override
    public String toString(){
        return stateId.toString();
    }
}

可以看出State聚合了多个状态转移(Transition) ,

状态转移是通过EventTransitions类来进行封装管理的,源码如下:

/**
 * EventTransitions
 *
 * 同一个Event可以触发多个Transitions,https://github.com/alibaba/COLA/pull/158
 *
 * @author Frank Zhang
 * @date 2021-05-28 5:17 PM
 */
public class EventTransitions<S,E,C> {
    private HashMap<E, List<Transition<S,E,C>>> eventTransitions;

    public EventTransitions(){
        eventTransitions = new HashMap<>();
    }

    public void put(E event, Transition<S, E, C> transition){
        if(eventTransitions.get(event) == null){
            List<Transition<S,E,C>> transitions = new ArrayList<>();
            transitions.add(transition);
            eventTransitions.put(event, transitions);
        }
        else{
            List existingTransitions = eventTransitions.get(event);
            verify(existingTransitions, transition);
            existingTransitions.add(transition);
        }
    }

    /**
     * Per one source and target state, there is only one transition is allowed
     * @param existingTransitions
     * @param newTransition
     */
    private void verify(List<Transition<S,E,C>> existingTransitions, Transition<S,E,C> newTransition) {
        for (Transition transition : existingTransitions) {
            if (transition.equals(newTransition)) {
                throw new StateMachineException(transition + " already Exist, you can not add another one");
            }
        }
    }

    public List<Transition<S,E,C>> get(E event){
        return eventTransitions.get(event);
    }

    public List<Transition<S,E,C>> allTransitions(){
        List<Transition<S,E,C>> allTransitions = new ArrayList<>();
        for(List<Transition<S,E,C>> transitions : eventTransitions.values()){
            allTransitions.addAll(transitions);
        }
        return allTransitions;
    }
}

2.1.3 Transition接口

接口定义如下:

/**
 * {@code Transition} is something what a state machine associates with a state
 * changes.
 *
 * @author Frank Zhang
 *
 * @param <S> the type of state
 * @param <E> the type of event
 * @param <C> the type of user defined context, which is used to hold application data
 *
 * @date 2020-02-07 2:20 PM
 */
public interface Transition<S, E, C>{
    /**
     * Gets the source state of this transition.
     *
     * @return the source state
     */
    State<S,E,C> getSource();

    void setSource(State<S, E, C> state);

    E getEvent();

    void setEvent(E event);

    void setType(TransitionType type);
    /**
     * Gets the target state of this transition.
     *
     * @return the target state
     */
    State<S,E,C> getTarget();

    void setTarget(State<S, E, C> state);

    /**
     * Gets the guard of this transition.
     *
     * @return the guard
     */
    Condition<C> getCondition();

    void setCondition(Condition<C> condition);

    Action<S,E,C> getAction();

    void setAction(Action<S, E, C> action);

    /**
     * Do transition from source state to target state.
     *
     * @return the target state
     */

    State<S, E, C> transit(C ctx, boolean checkCondition);
    /**
     * Verify transition correctness
     */
    void verify();
}

具体实现如下:

/**
 * TransitionImpl。
 *
 * This should be designed to be immutable, so that there is no thread-safe risk
 *
 * @author Frank Zhang
 * @date 2020-02-07 10:32 PM
 */
public class TransitionImpl<S,E,C> implements Transition<S,E,C> {

    private State<S, E, C> source;

    private State<S, E, C> target;

    private E event;

    private Condition<C> condition;

    private Action<S,E,C> action;

    private TransitionType type = TransitionType.EXTERNAL;

    @Override
    public State<S, E, C> getSource() {
        return source;
    }

    @Override
    public void setSource(State<S, E, C> state) {
        this.source = state;
    }

    @Override
    public E getEvent() {
        return this.event;
    }

    @Override
    public void setEvent(E event) {
        this.event = event;
    }

    @Override
    public void setType(TransitionType type) {
        this.type = type;
    }

    @Override
    public State<S, E, C> getTarget() {
        return this.target;
    }

    @Override
    public void setTarget(State<S, E, C> target) {
        this.target = target;
    }

    @Override
    public Condition<C> getCondition() {
        return this.condition;
    }

    @Override
    public void setCondition(Condition<C> condition) {
        this.condition = condition;
    }

    @Override
    public Action<S, E, C> getAction() {
        return this.action;
    }

    @Override
    public void setAction(Action<S, E, C> action) {
        this.action = action;
    }

    @Override
    public State<S, E, C> transit(C ctx, boolean checkCondition) {
        Debugger.debug("Do transition: "+this);
        this.verify();
        if (!checkCondition || condition == null || condition.isSatisfied(ctx)) {
            if(action != null){
                action.execute(source.getId(), target.getId(), event, ctx);
            }
            return target;
        }

        Debugger.debug("Condition is not satisfied, stay at the "+source+" state ");
        return source;
    }

    @Override
    public final String toString() {
        return source + "-[" + event.toString() +", "+type+"]->" + target;
    }

    @Override
    public boolean equals(Object anObject){
        if(anObject instanceof Transition){
            Transition other = (Transition)anObject;
            if(this.event.equals(other.getEvent())
                    && this.source.equals(other.getSource())
                    && this.target.equals(other.getTarget())){
                return true;
            }
        }
        return false;
    }

    @Override
    public void verify() {
        if(type== TransitionType.INTERNAL && source != target) {
            throw new StateMachineException(String.format("Internal transition source state '%s' " +
                    "and target state '%s' must be same.", source, target));
        }
    }
}

可以看出,Transition聚合了源State、目标State、Event、Condition、Action;

在上述方法transit中,Transition在满足条件Condition的条件下,执行了具体动作Action,并且返回目标State;

2.1.4 StateMachine接口

/**
 * StateMachine
 *
 * @author Frank Zhang
 *
 * @param <S> the type of state
 * @param <E> the type of event
 * @param <C> the user defined context
 * @date 2020-02-07 2:13 PM
 */
public interface StateMachine<S, E, C> extends Visitable{

    /**
     * Verify if an event {@code E} can be fired from current state {@code S}
     * @param sourceStateId
     * @param event
     * @return
     */
    boolean verify(S sourceStateId,E event);

    /**
     * Send an event {@code E} to the state machine.
     *
     * @param sourceState the source state
     * @param event the event to send
     * @param ctx the user defined context
     * @return the target state
     */
     S fireEvent(S sourceState, E event, C ctx);

    /**
     * MachineId is the identifier for a State Machine
     * @return
     */
    String getMachineId();

    /**
     * Use visitor pattern to display the structure of the state machine
     */
    void showStateMachine();

    String generatePlantUML();
}

状态机具体实现:

/**
 * For performance consideration,
 * The state machine is made "stateless" on purpose.
 * Once it's built, it can be shared by multi-thread
 * <p>
 * One side effect is since the state machine is stateless, we can not get current state from State Machine.
 *
 * @author Frank Zhang
 * @date 2020-02-07 5:40 PM
 */
public class StateMachineImpl<S, E, C> implements StateMachine<S, E, C> {

    private String machineId;

    private final Map<S, State<S, E, C>> stateMap;

    private boolean ready;

    private FailCallback<S, E, C> failCallback;

    public StateMachineImpl(Map<S, State<S, E, C>> stateMap) {
        this.stateMap = stateMap;
    }

    @Override
    public boolean verify(S sourceStateId, E event) {
        isReady();

        State sourceState = getState(sourceStateId);

        List<Transition<S, E, C>> transitions = sourceState.getEventTransitions(event);

        return transitions != null && transitions.size() != 0;
    }

    @Override
    public S fireEvent(S sourceStateId, E event, C ctx) {
        isReady();
        Transition<S, E, C> transition = routeTransition(sourceStateId, event, ctx);

        if (transition == null) {
            Debugger.debug("There is no Transition for " + event);
            failCallback.onFail(sourceStateId, event, ctx);
            return sourceStateId;
        }

        return transition.transit(ctx, false).getId();
    }

    private Transition<S, E, C> routeTransition(S sourceStateId, E event, C ctx) {
        State sourceState = getState(sourceStateId);

        List<Transition<S, E, C>> transitions = sourceState.getEventTransitions(event);

        if (transitions == null || transitions.size() == 0) {
            return null;
        }

        Transition<S, E, C> transit = null;
        for (Transition<S, E, C> transition : transitions) {
            if (transition.getCondition() == null) {
                transit = transition;
            } else if (transition.getCondition().isSatisfied(ctx)) {
                transit = transition;
                break;
            }
        }

        return transit;
    }

    private State getState(S currentStateId) {
        State state = StateHelper.getState(stateMap, currentStateId);
        if (state == null) {
            showStateMachine();
            throw new StateMachineException(currentStateId + " is not found, please check state machine");
        }
        return state;
    }

    private void isReady() {
        if (!ready) {
            throw new StateMachineException("State machine is not built yet, can not work");
        }
    }

    @Override
    public String accept(Visitor visitor) {
        StringBuilder sb = new StringBuilder();
        sb.append(visitor.visitOnEntry(this));
        for (State state : stateMap.values()) {
            sb.append(state.accept(visitor));
        }
        sb.append(visitor.visitOnExit(this));
        return sb.toString();
    }

    @Override
    public void showStateMachine() {
        SysOutVisitor sysOutVisitor = new SysOutVisitor();
        accept(sysOutVisitor);
    }

    @Override
    public String generatePlantUML() {
        PlantUMLVisitor plantUMLVisitor = new PlantUMLVisitor();
        return accept(plantUMLVisitor);
    }

    @Override
    public String getMachineId() {
        return machineId;
    }

    public void setMachineId(String machineId) {
        this.machineId = machineId;
    }

    public void setReady(boolean ready) {
        this.ready = ready;
    }

    public void setFailCallback(FailCallback<S, E, C> failCallback) {
        this.failCallback = failCallback;
    }
}

状态机方法fireEvent的执行逻辑如下:

  1. 通过routeTransition方法,获取目标转移Transition(只会返回一个)

  2. 执行目标转移Transition的转移方法transit,并返回目标State

在方法routeTransition中:

  1. 首先根据源State和Event获取关联的转移列表
  2. 遍历转移列表,返回命中转移条件Condition的转移

2.2 Builder模式

上述cola状态机语义模型的源码就解析完成了,在实际使用时,需要将语义模型进行组合构造编排,这里是通过Builder模式来完成的,下面进行展开说明;

2.2.1 StateMachine Builder模式

StateMachineBuilder顶层接口定义如下:

/**
 * StateMachineBuilder
 *
 * @author Frank Zhang
 * @date 2020-02-07 5:32 PM
 */
public interface StateMachineBuilder<S, E, C> {
    /**
     * Builder for one transition
     *
     * @return External transition builder
     */
    ExternalTransitionBuilder<S, E, C> externalTransition();

    /**
     * Builder for multiple transitions
     *
     * @return External transition builder
     */
    ExternalTransitionsBuilder<S, E, C> externalTransitions();

    /**
     * Start to build internal transition
     *
     * @return Internal transition builder
     */
    InternalTransitionBuilder<S, E, C> internalTransition();

    /**
     * set up fail callback, default do nothing {@code NumbFailCallbackImpl}
     *
     * @param callback
     */
    void setFailCallback(FailCallback<S, E, C> callback);

    StateMachine<S, E, C> build(String machineId);

}

具体实现如下:

/**
 * StateMachineBuilderImpl
 *
 * @author Frank Zhang
 * @date 2020-02-07 9:40 PM
 */
public class StateMachineBuilderImpl<S, E, C> implements StateMachineBuilder<S, E, C> {

    /**
     * StateMap is the same with stateMachine, as the core of state machine is holding reference to states.
     */
    private final Map<S, State<S, E, C>> stateMap = new ConcurrentHashMap<>();
    private final StateMachineImpl<S, E, C> stateMachine = new StateMachineImpl<>(stateMap);
    private FailCallback<S, E, C> failCallback = new NumbFailCallback<>();

    @Override
    public ExternalTransitionBuilder<S, E, C> externalTransition() {
        return new TransitionBuilderImpl<>(stateMap, TransitionType.EXTERNAL);
    }

    @Override
    public ExternalTransitionsBuilder<S, E, C> externalTransitions() {
        return new TransitionsBuilderImpl<>(stateMap, TransitionType.EXTERNAL);
    }

    @Override
    public InternalTransitionBuilder<S, E, C> internalTransition() {
        return new TransitionBuilderImpl<>(stateMap, TransitionType.INTERNAL);
    }

    @Override
    public void setFailCallback(FailCallback<S, E, C> callback) {
        this.failCallback = callback;
    }

    @Override
    public StateMachine<S, E, C> build(String machineId) {
        stateMachine.setMachineId(machineId);
        stateMachine.setReady(true);
        stateMachine.setFailCallback(failCallback);
        StateMachineFactory.register(stateMachine);
        return stateMachine;
    }

}

StateMachineBuilder定义中:

  • 通过build方法完成状态机实例的创建 
  • 通过externalTransition方法完成外部转移的构造
  • 通过internalTransition方法完成内部转移的构造

在build方法中,构造StateMachine实例,并注册到StateMachineFactory中 ,然后返回StateMachine实例,完成构建;

对于转移Transition的构造,下面以externalTransition返回的ExternalTransitionBuilder为例进行具体分析,内部转移同理不再展开;

2.2.2 ExternalTransitionBuilder---转移构造Builder模式

2.2.2.1 链式调用顺序保障

在上述状态机使用实例中,是通过链式编程来完成外部转移构造的,如下:

这里通过接口约束,限制了:

  1. externalTransition方法之后只能调用from方法
  2. from方法之后只能调用to方法等调用顺序
  3. ......依次类推

内部原理是:

  1. externalTransition方法返回了ExternalTransitionBuilder接口,而ExternalTransitionBuilder接口只定义了from方法,并且返回From接口
  2. From接口中又只定义了to方法,并且返回To接口
  3. ......依次类推

相关接口定义说明如下:

/**
 * ExternalTransitionBuilder
 *
 * @author Frank Zhang
 * @date 2020-02-07 6:11 PM
 */
public interface ExternalTransitionBuilder<S, E, C> {
    /**
     * Build transition source state.
     * @param stateId id of state
     * @return from clause builder
     */
    From<S, E, C> from(S stateId);

}
/**
 * From
 *
 * @author Frank Zhang
 * @date 2020-02-07 6:13 PM
 */
public interface From<S, E, C> {
    /**
     * Build transition target state and return to clause builder
     * @param stateId id of state
     * @return To clause builder
     */
    To<S, E, C> to(S stateId);

}
/**
 * To
 *
 * @author Frank Zhang
 * @date 2020-02-07 6:14 PM
 */
public interface To<S, E, C> {
    /**
     * Build transition event
     * @param event transition event
     * @return On clause builder
     */
    On<S, E, C> on(E event);
}
public interface On<S, E, C> extends When<S, E, C>{
    /**
     * Add condition for the transition
     * @param condition transition condition
     * @return When clause builder
     */
    When<S, E, C> when(Condition<C> condition);
}
public interface When<S, E, C>{
    /**
     * Define action to be performed during transition
     *
     * @param action performed action
     */
    void perform(Action<S, E, C> action);
}

 通过上述严格的顺序调用,保证了Transition构造的正确性和易读性;

2.2.2.2 链式调用具体实现

下面看一下上述链式调用相关接口的具体实现逻辑:

ExternalTransitionBuilder的具体实现类如下:

/**
 * TransitionBuilderImpl
 *
 * @author Frank Zhang
 * @date 2020-02-07 10:20 PM
 */
class TransitionBuilderImpl<S,E,C> extends AbstractTransitionBuilder<S,E,C> implements ExternalTransitionBuilder<S,E,C>, InternalTransitionBuilder<S,E,C> {


    private State<S, E, C> source;
    private Transition<S, E, C> transition;

    public TransitionBuilderImpl(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {
        super(stateMap, transitionType);
    }

    @Override
    public From<S, E, C> from(S stateId) {
        source = StateHelper.getState(stateMap, stateId);
        return this;
    }

    @Override
    public To<S, E, C> within(S stateId) {
        source = target = StateHelper.getState(stateMap, stateId);
        return this;
    }

    @Override
    public When<S, E, C> when(Condition<C> condition) {
        transition.setCondition(condition);
        return this;
    }

    @Override
    public On<S, E, C> on(E event) {
        transition = source.addTransition(event, target, transitionType);
        return this;
    }

    @Override
    public void perform(Action<S, E, C> action) {
        transition.setAction(action);
    }


}

其中AbstractTransitionBuilder抽象类具体实现如下,实现了接口From、To、On:

 abstract class AbstractTransitionBuilder<S,E,C> implements  From<S,E,C>,On<S,E,C>,To<S,E,C>{

    final Map<S, State<S, E, C>> stateMap;

    protected State<S, E, C> target;

    final TransitionType transitionType;

    public AbstractTransitionBuilder(Map<S, State<S, E, C>> stateMap, TransitionType transitionType) {
        this.stateMap = stateMap;
        this.transitionType = transitionType;
    }
    @Override
    public To<S, E, C> to(S stateId) {
        target = StateHelper.getState(stateMap, stateId);
        return this;
    }
}

 上述用到的StateHelper帮助类说明如下:

public class StateHelper {
    public static <S, E, C> State<S, E, C> getState(Map<S, State<S, E, C>> stateMap, S stateId){
        State<S, E, C> state = stateMap.get(stateId);
        if (state == null) {
            state = new StateImpl<>(stateId);
            stateMap.put(stateId, state);
        }
        return state;
    }
}

下面对链式调用执行过程具体说明如下: 

  1. from方法实现中,通过StateHelper帮助类完成源State的创建;
  2.  to方法实现中,同样通过StateHelper帮助类完成目标State的创建;
  3. on方法实现中,在源State中添加新的转移;
  4. when方法实现中,对构造的转移添加转移条件Condition;
  5. perform方法实现中,对构造的转移添加具体动作Action;

最后通过状态机build方法,完成状态机的构造并注入所有State列表;

至此,状态机实例、状态机包含的状态、状态关联的所有转移都构造完毕,后续就可以通过状态机的触发方法fireEvent完成状态转移了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/110006.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring中Bean的完整生命周期!(Bean实例化的流程,Spring后处理器,循环依赖解释及解决方法)附案例演示

Bean实例化的基本流程 加载xml配置文件&#xff0c;解析获取配置中的每个的信息&#xff0c;封装成一个个的BeanDefinition对象将BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中ApplicationContext底层遍历beanDefinitionMap&#xff0c…

解决计算机msvcp120.dll文件丢失的5种方法,亲测有效

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp120.dll丢失”。这个错误提示可能会给我们带来很大的困扰&#xff0c;影响我们的正常使用。本文将详细介绍msvcp120.dll丢失的原因、解决方法以及预防措施&#xff0c;帮助大家更好地…

3D LUT 滤镜 shader 源码分析

最近在做滤镜相关的渲染学习&#xff0c;目前大部分 LUT 滤镜代码实现都是参考由 GPUImage 提供的 LookupFilter 的逻辑&#xff0c;整个代码实现不多。参考网上的博文也有各种解释&#xff0c;参考了大量博文之后终于理解了&#xff0c;所以自己重新整理了一份&#xff0c;方便…

selenium工作原理和反爬分析

一、 Selenium Selenium是最广泛使用的开源Web UI(用户界面)自动化测试套件之一&#xff0c;支持并行测试执行。Selenium通过使用特定于每种语言的驱动程序支持各种编程语言。Selenium支持的语言包括C#&#xff0c;Java&#xff0c;Perl&#xff0c;PHP&#xff0c;Python和Ru…

Linux——Linux权限

Linux权限 前言一、shell命令以及运行原理二、Linux权限的概念Linux权限管理文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性&#xff09;文件权限值的表示方法文件访问权限的相关设置方法 file指令目录的权限粘滞位 总结 前言 linux的学习…

基本微信小程序的体检预约小程序

项目介绍 我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;体检预约系统小程序被用户普遍使用&#xff0c;为方便用户…

LabVIEW开发基于图像处理的车牌检测系统

LabVIEW开发基于图像处理的车牌检测系统 自动车牌识别的一般步骤是图像采集、去除噪声的预处理、车牌定位、字符分割和字符识别。结果主要取决于所采集图像的质量。在不同照明条件下获得的图像具有不同的结果。在要使用的预处理技术中&#xff0c;必须将彩色图像转换为灰度&am…

【PyQt学习篇 · ⑧】:QWidget - 窗口特定操作

文章目录 图标标题不透明度窗口状态最大化和最小化窗口标志案例 图标 setWindowIcon(QIcon("resource/header_icon.png"))&#xff1a;该函数用于设置QWidget的窗口图标。可以为窗口设置一个图标&#xff0c;以显示在窗口标题栏、任务栏或窗口管理器中。 windowIcon…

识别flink的反压源头

背景 flink中最常见的问题就是反压&#xff0c;这种情况下我们要正确的识别导致反压的真正的源头&#xff0c;本文就简单看下如何正确识别反压的源头 反压的源头 首先我们必须意识到现实中轻微的反压是没有必要去优化的&#xff0c;因为这种情况下是由于偶尔的流量峰值,Task…

Linux 音频驱动实验

目录 音频接口简介为何需要音频编解码芯片&#xff1f;WM8960 简介I2S 总线接口I.MX6ULL SAI 简介 硬件原理图分析音频驱动使能修改设备树使能内核的WM8960 驱动alsa-lib 移植alsa-utils 移植 声卡设置与测试amixer 使用方法音乐播放测试MIC 录音测试LINE IN 录音测试 开机自动…

论文范文:论基于架构的软件设计方法及应用

注意:范文只适用于帮助大家打开写作思路,并不能作为素材直接用于平时练习、考试中。考试中直接使用范文的素材,会有被认定为雷同卷的风险。 摘要: 2022年4月,本人所在单位计划研发生态集装箱管理控制平台项目。该平台主要用于与现有公司生态集装箱产品做对接,达到远程控制…

【Leetcode】【简单】13. 罗马数字转整数

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/roman-to-integer/description/ …

用图说话——流程图进阶

目录 一、基本流程图 二、时序流程图 一、基本流程图 经常阅读歪果仁绘制的流程图&#xff0c;感觉比较规范&#xff0c;自己在工作中也尝试用他们思维来绘图&#xff0c;这是一个小栗子&#xff1a; 二、时序流程图 在进行Detail设计过程中&#xff0c;一般的绘图软件显得…

【Xilinx Kintex-7 Virtex-7 LVDS bank电压】

各种介绍很多&#xff0c;也都写的似乎很长很详细&#xff0c;但有错误。 详细的查阅Xilinx 论坛 43989 核心 总结一下就是Xilinx 7serious 的FPGA ,你如果要配置成LVDS,这的LVDS是正儿八经的那种&#xff0c;那么FPGA 这块你只需要记住两点就可以。 第一&#xff0c;假如你…

开放式耳机推荐排行榜、开放式耳机性价比推荐

随着无线耳机越来越普及&#xff0c;人们对于耳机的要求也越来越高。传统的入耳式耳机虽然音质好&#xff0c;但是长时间佩戴容易引起耳部不适&#xff0c;甚至可能导致听力损失。为此大家都开始选择入手舒适、安全的开放式耳机&#xff0c;现在耳机市场&#xff0c;各种品牌、…

脚本木马编写

PHP小马编写 小马用waf扫描&#xff0c;没扫描出来有风险。 小马过waf之后用echo $_SERVER[DOCUMENT_ROOT]获得当前运行脚本所在的文档根目录。&#xff0c;然后在上传大马工具。 $_SERVER&#xff0c;参考&#xff1a;PHP $_SERVER详解 小马编写二次加密 现在是可以被安全…

98. 验证二叉搜索树

题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 解题思路&#xff1a; 二叉搜索树的定义&#xff1a; 二叉搜索树或者是一颗空树&#xff0c;或者是具有如下性质的二叉树&#xff1a; 若它的左子树不空&#xff0c;则左子树上…

OpenCV官方教程中文版 —— Hough 直线变换

OpenCV官方教程中文版 —— Hough 直线变换 前言一、原理二、OpenCV 中的霍夫变换三、Probabilistic Hough Transform 前言 目标 • 理解霍夫变换的概念 • 学习如何在一张图片中检测直线 • 学习函数&#xff1a;cv2.HoughLines()&#xff0c;cv2.HoughLinesP() 一、原理…

C++ priority_queue 的使用

1. priority_queue 的介绍 下面是 priority_queue 的介绍&#xff0c;来自于&#xff1a;&#x1f3f9;priority_queue - C Reference (cplusplus.com) 的中文翻译&#xff0c;您可以尝试看看。 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一…

实战 | 记一次红队打的逻辑漏洞

八月初参加某市演练时遇到一个典型的逻辑漏洞&#xff0c;可以绕过验证码并且重置任意用户的密码。 首先访问页面&#xff0c;用户名处输入账号会回显用户名称&#xff0c;输入admin会回显系统管理员。&#xff08;hvv的时候蓝队响应太快了&#xff0c;刚把admin的权限拿到了&a…