EasyRule源码:工厂方法模式之规则创建源码分析

news2024/12/24 3:30:02

目录

1.规则创建方式

1.1.@Rule注解

1.2.链式编程

1.3.表达式方式

1.4.文件脚本DSL方式

2.创建的规则类(产品类)

3.规则工厂类

3.1 RuleDefinition类

3.2 组合规则创建

3.3 单一规则创建


EasyRule框架的源码解析见上篇文章:EasyRule源码:EasyRule框架源码分析

本文主要对EasyRule框架中应用的工厂方法模式---规则创建进行源码解析;

1.规则创建方式

EasyRule框架支持的创建规则的方法有很多种,如下是几种规则创建的方式:

1.1.@Rule注解

利用注解方式完成规则的创建:

@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {

    @Condition
    public boolean itRains(@Fact("rain") boolean rain) {
        return rain;
    }
    
    @Action
    public void takeAnUmbrella() {
        System.out.println("It rains, take an umbrella!");
    }
}

1.2.链式编程

Rule weatherRule = new RuleBuilder()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when(facts -> facts.get("rain").equals(true))
        .then(facts -> System.out.println("It rains, take an umbrella!"))
        .build();

1.3.表达式方式

Rule weatherRule = new MVELRule()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when("rain == true")
        .then("System.out.println(\"It rains, take an umbrella!\");");

1.4.文件脚本DSL方式

weather-rule.yml文件定义:

name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
  - "System.out.println(\"It rains, take an umbrella!\");"

或者weather-rule.json文件定义:

[
  {
    "name": "weather rule",
    "description": "if it rains then take an umbrella",
    "priority": 1,
    "condition": "rain == true",
    "actions": [
      "System.out.println(\"It rains, take an umbrella!\");"
    ]
  }
]

其中,前3种方式都是通过Java编码的方式实现规则创建,第4种文件脚本DSL方式通过指定json或yml文件的方式完成规则创建,该种方式的好处包括:

  • 规则创建更加简单易懂
  • 可以支持规则热发布

在EasyRule框架源码中,对文件脚本DSL方式的解析是通过巧妙利用工厂方法模式来完成规则创建的,这里的抽象工厂类就是AbstractRuleFactory,创建的产品类是Rule具体实现类,下面重点从产品类和工厂类角度对该部分进行源码解析;

2.创建的规则类(产品类)

具体的规则类整体类图如下:

规则类包括单一规则(SpELRule&MVELRule&JexlRule)和组合规则(CompositeRule),说明如下:

BasicRule:Rule接口的基础实现类,管理规则名称、描述和优先级

DefaultRule:默认规则实现类,包含Condition和多个Action

SpELRule&MVELRule&JexlRule:支持SpEL、MVEL、Jexl表达式定义的Condition和Action

CompositeRule:组合规则,对多个规则组合管理

ActivationRuleGroup&ConditionalRuleGroup&UnitRuleGroup:封装不同的组合规则管理策略

 以SpELRule为例说明,SpELRule源码如下:

/**
 * A {@link Rule} implementation that uses 
 * <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions">SpEL</a>
 * to evaluate and execute the rule.
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
public class SpELRule extends BasicRule {

    private Condition condition = Condition.FALSE;
    private final List<Action> actions = new ArrayList<>();
    private final ParserContext parserContext;
    private BeanResolver beanResolver;

    /**
     * Create a new SpEL rule.
     */
    public SpELRule() {
        this(ParserContext.TEMPLATE_EXPRESSION);
    }

    /**
     * Create a new SpEL rule.
     * 
     * @param parserContext used when parsing expressions
     */
    public SpELRule(ParserContext parserContext) {
        super(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
        this.parserContext = parserContext;
    }

    /**
     * Create a new SpEL rule.
     *
     * @param beanResolver used to resolve bean references in expressions
     */
    public SpELRule(BeanResolver beanResolver) {
        super(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
        this.parserContext = ParserContext.TEMPLATE_EXPRESSION;
        this.beanResolver = beanResolver;
    }

    /**
     * Create a new SpEL rule.
     *
     * @param parserContext used when parsing expressions
     * @param beanResolver used to resolve bean references in expressions
     */
    public SpELRule(ParserContext parserContext, BeanResolver beanResolver) {
        super(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
        this.parserContext = parserContext;
        this.beanResolver = beanResolver;
    }

    /**
     * Set rule name.
     *
     * @param name of the rule
     * @return this rule
     */
    public SpELRule name(String name) {
        this.name = name;
        return this;
    }

    /**
     * Set rule description.
     *
     * @param description of the rule
     * @return this rule
     */
    public SpELRule description(String description) {
        this.description = description;
        return this;
    }

    /**
     * Set rule priority.
     *
     * @param priority of the rule
     * @return this rule
     */
    public SpELRule priority(int priority) {
        this.priority = priority;
        return this;
    }

    /**
     * Specify the rule's condition as SpEL expression.
     * @param condition of the rule
     * @return this rule
     */
    public SpELRule when(String condition) {
        this.condition = new SpELCondition(condition, parserContext, beanResolver);
        return this;
    }

    /**
     * Add an action specified as an SpEL expression to the rule.
     * @param action to add to the rule
     * @return this rule
     */
    public SpELRule then(String action) {
        this.actions.add(new SpELAction(action, parserContext, beanResolver));
        return this;
    }

    @Override
    public boolean evaluate(Facts facts) {
        return condition.evaluate(facts);
    }

    @Override
    public void execute(Facts facts) throws Exception {
        for (Action action : actions) {
            action.execute(facts);
        }
    }
}

如上,在SpELRule表达式实现中,支持SpEL表达式定义的Condition和Action,并分别实现了规则条件的解析和规则动作的执行;

3.规则工厂类

上述对json、yml 文件脚本进行解析并完成具体规则的创建都是通过规则工厂类来完成的;

规则工厂类图如下:

首先看一下AbstractRuleFactory源码:

/**
 * Base class for rule factories.
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
public abstract class AbstractRuleFactory {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRuleFactory.class);

    private static final List<String> ALLOWED_COMPOSITE_RULE_TYPES = Arrays.asList(
            UnitRuleGroup.class.getSimpleName(),
            ConditionalRuleGroup.class.getSimpleName(),
            ActivationRuleGroup.class.getSimpleName()
    );

    protected Rule createRule(RuleDefinition ruleDefinition) {
        if (ruleDefinition.isCompositeRule()) {
            return createCompositeRule(ruleDefinition);
        } else {
            return createSimpleRule(ruleDefinition);
        }
    }

    protected abstract Rule createSimpleRule(RuleDefinition ruleDefinition);

    protected Rule createCompositeRule(RuleDefinition ruleDefinition) {
        if (ruleDefinition.getCondition() != null) {
            LOGGER.warn(
                    "Condition '{}' in composite rule '{}' of type {} will be ignored.",
                    ruleDefinition.getCondition(),
                    ruleDefinition.getName(),
                    ruleDefinition.getCompositeRuleType());
        }
        if (ruleDefinition.getActions() != null && !ruleDefinition.getActions().isEmpty()) {
            LOGGER.warn(
                    "Actions '{}' in composite rule '{}' of type {} will be ignored.",
                    ruleDefinition.getActions(),
                    ruleDefinition.getName(),
                    ruleDefinition.getCompositeRuleType());
        }
        CompositeRule compositeRule;
        String name = ruleDefinition.getName();
        switch (ruleDefinition.getCompositeRuleType()) {
            case "UnitRuleGroup":
                compositeRule = new UnitRuleGroup(name);
                break;
            case "ActivationRuleGroup":
                compositeRule = new ActivationRuleGroup(name);
                break;
            case "ConditionalRuleGroup":
                compositeRule = new ConditionalRuleGroup(name);
                break;
            default:
                throw new IllegalArgumentException("Invalid composite rule type, must be one of " + ALLOWED_COMPOSITE_RULE_TYPES);
        }
        compositeRule.setDescription(ruleDefinition.getDescription());
        compositeRule.setPriority(ruleDefinition.getPriority());

        for (RuleDefinition composingRuleDefinition : ruleDefinition.getComposingRules()) {
            compositeRule.addRule(createRule(composingRuleDefinition));
        }

        return compositeRule;
    }

}

在上述源码中,分别通过方法createSimpleRule和方法createCompositeRule完成单一规则和组合规则的创建;

下面分别从单一规则创建和组合规则创建的角度分析工厂类具体的创建过程;

在分析工厂类创建逻辑之前,我们注意到工厂类的创建方法参数是RuleDefinition,首先看一下RuleDefinition的构造;

3.1 RuleDefinition类

RuleDefinition表示规则描述类,封装了规则的名称、描述、优先级以及包含的条件和执行的动作等;

RuleDefinition的定义如下:

/**
 * Rule definition as defined in a rule descriptor.
 * This class encapsulates the static definition of a {@link Rule}.
 *
 * Rule definitions are produced by a {@code RuleDefinitionReader}s
 * and consumed by rule factories to create rules.
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
public class RuleDefinition {

    private String name = Rule.DEFAULT_NAME;
    private String description = Rule.DEFAULT_DESCRIPTION;
    private int priority = Rule.DEFAULT_PRIORITY;
    private String condition;
    private List<String> actions = new ArrayList<>();
    private List<RuleDefinition> composingRules = new ArrayList<>();
    private String compositeRuleType;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getCondition() {
        return condition;
    }

    public void setCondition(String condition) {
        this.condition = condition;
    }

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public List<String> getActions() {
        return actions;
    }

    public void setActions(List<String> actions) {
        this.actions = actions;
    }

    public void setComposingRules(List<RuleDefinition> composingRuleDefinitions) {
        this.composingRules = composingRuleDefinitions;
    }

    public void setCompositeRuleType(String compositeRuleType) {
        this.compositeRuleType = compositeRuleType;
    }

    public String getCompositeRuleType() {
        return compositeRuleType;
    }

    public List<RuleDefinition> getComposingRules() {
        return composingRules;
    }

    public boolean isCompositeRule() {
        return !composingRules.isEmpty();
    }
}

RuleDefinition的构造是通过RuleDefinitionReader完成的,RuleDefinitionReader的定义如下:

/**
 * Strategy interface for {@link RuleDefinition} readers.
 *
 * @see JsonRuleDefinitionReader
 * @see YamlRuleDefinitionReader
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
@FunctionalInterface
public interface RuleDefinitionReader {

    /**
     * Read a list of rule definitions from a rule descriptor.
     *
     * <strong> The descriptor is expected to contain a collection of rule definitions
     * even for a single rule.</strong>
     *
     * @param reader of the rules descriptor
     * @return a list of rule definitions
     * @throws Exception if a problem occurs during rule definition reading
     */
    List<RuleDefinition> read(Reader reader) throws Exception;

}

read方法接受Reader输入,并返回解析结果RuleDefinition列表;

EasyRule框架源码中,提供了分别针对.json文件和.yml文件的Reader实现类: 

JsonRuleDefinitionReader:完成.json文件的解析,并构造RuleDefinition

YamlRuleDefinitionReader:完成.yml文件的解析,并构造RuleDefinition

RuleDefinitionReader的整体类图如下: 

具体的解析逻辑封装到了抽象类接口中:

/**
 * Base class for {@link RuleDefinitionReader}s.
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
public abstract class AbstractRuleDefinitionReader implements RuleDefinitionReader {

    public List<RuleDefinition> read(Reader reader) throws Exception {
        List<RuleDefinition> ruleDefinitions = new ArrayList<>();
        Iterable<Map<String, Object>> rules = loadRules(reader);
        for (Map<String, Object> rule : rules) {
            ruleDefinitions.add(createRuleDefinition(rule));
        }
        return ruleDefinitions;
    }

    /**
     * Load rules from the given reader as an iterable of Maps.
     *
     * @param reader to read rules from
     * @return an iterable of rule Maps
     * @throws Exception if unable to load rules
     */
    protected abstract Iterable<Map<String, Object>> loadRules(Reader reader) throws Exception;

    /**
     * Create a rule definition.
     *
     * @param map of rule properties
     * @return a rule definition
     */
    protected RuleDefinition createRuleDefinition(Map<String, Object> map) {
        RuleDefinition ruleDefinition = new RuleDefinition();

        String name = (String) map.get("name");
        ruleDefinition.setName(name != null ? name : Rule.DEFAULT_NAME);

        String description = (String) map.get("description");
        ruleDefinition.setDescription(description != null ? description : Rule.DEFAULT_DESCRIPTION);

        Integer priority = (Integer) map.get("priority");
        ruleDefinition.setPriority(priority != null ? priority : Rule.DEFAULT_PRIORITY);

        String compositeRuleType = (String) map.get("compositeRuleType");

        String condition = (String) map.get("condition");
        if (condition == null && compositeRuleType == null) {
            throw new IllegalArgumentException("The rule condition must be specified");
        }
        ruleDefinition.setCondition(condition);

        List<String> actions = (List<String>) map.get("actions");
        if ((actions == null || actions.isEmpty()) && compositeRuleType == null) {
            throw new IllegalArgumentException("The rule action(s) must be specified");
        }
        ruleDefinition.setActions(actions);

        List<Object> composingRules = (List<Object>) map.get("composingRules");
        if ((composingRules != null && !composingRules.isEmpty()) && compositeRuleType == null) {
            throw new IllegalArgumentException("Non-composite rules cannot have composing rules");
        } else if ((composingRules == null || composingRules.isEmpty()) && compositeRuleType != null) {
            throw new IllegalArgumentException("Composite rules must have composing rules specified");
        } else if (composingRules != null) {
            List<RuleDefinition> composingRuleDefinitions = new ArrayList<>();
            for (Object rule : composingRules) {
                Map<String, Object> composingRuleMap = (Map<String, Object>) rule;
                composingRuleDefinitions.add(createRuleDefinition(composingRuleMap));
            }
            ruleDefinition.setComposingRules(composingRuleDefinitions);
            ruleDefinition.setCompositeRuleType(compositeRuleType);
        }

        return ruleDefinition;
    }
}

抽象方法loadRules交由子类具体实现,JsonRuleDefinitionReader和YamlRuleDefinitionReader的具体实现分别如下:

/**
 * Rule definition reader based on <a href="https://github.com/FasterXML/jackson">Jackson</a>.
 *
 * This reader expects an array of rule definitions as input even for a single rule. For example:
 *
 * <pre>
 *     [{rule1}, {rule2}]
 * </pre>
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
@SuppressWarnings("unchecked")
public class JsonRuleDefinitionReader extends AbstractRuleDefinitionReader {

    private final ObjectMapper objectMapper;

    /**
     * Create a new {@link JsonRuleDefinitionReader}.
     */
    public JsonRuleDefinitionReader() {
        this(new ObjectMapper());
    }

    /**
     * Create a new {@link JsonRuleDefinitionReader}.
     *
     * @param objectMapper to use to read rule definitions
     */
    public JsonRuleDefinitionReader(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    protected Iterable<Map<String, Object>> loadRules(Reader reader) throws Exception {
        List<Map<String, Object>> rulesList = new ArrayList<>();
        Object[] rules = objectMapper.readValue(reader, Object[].class);
        for (Object rule : rules) {
            rulesList.add((Map<String, Object>) rule);
        }
        return rulesList;
    }

}
/**
 * Rule definition reader based on <a href="https://github.com/FasterXML/jackson-dataformats-text/tree/master/yaml">Jackson Yaml</a>.
 *
 * This reader expects a collection of rule definitions as input even for a single rule. For example:
 *
 * <pre>
 *     rule1
 *     ---
 *     rule2
 * </pre>
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
@SuppressWarnings("unchecked")
public class YamlRuleDefinitionReader extends AbstractRuleDefinitionReader {

    private final Yaml yaml;

    /**
     * Create a new {@link YamlRuleDefinitionReader}.
     */
    public YamlRuleDefinitionReader() {
        this(new Yaml());
    }

    /**
     * Create a new {@link YamlRuleDefinitionReader}.
     *
     * @param yaml to use to read rule definitions
     */
    public YamlRuleDefinitionReader(Yaml yaml) {
        this.yaml = yaml;
    }

    @Override
    protected Iterable<Map<String, Object>> loadRules(Reader reader) {
        List<Map<String, Object>> rulesList = new ArrayList<>();
        Iterable<Object> rules = yaml.loadAll(reader);
        for (Object rule : rules) {
            rulesList.add((Map<String, Object>) rule);
        }
        return rulesList;
    }
}

至此,借助RuleDefinitionReader完成了RuleDefinition的解析;

3.2 组合规则创建

在方法createRule实现中,根据解析结果中是否包含组合规则会路由到不同的创建逻辑;

包含组合规则的情况下,会构造具体的组合规则,如下:

    protected Rule createCompositeRule(RuleDefinition ruleDefinition) {
        if (ruleDefinition.getCondition() != null) {
            LOGGER.warn(
                    "Condition '{}' in composite rule '{}' of type {} will be ignored.",
                    ruleDefinition.getCondition(),
                    ruleDefinition.getName(),
                    ruleDefinition.getCompositeRuleType());
        }
        if (ruleDefinition.getActions() != null && !ruleDefinition.getActions().isEmpty()) {
            LOGGER.warn(
                    "Actions '{}' in composite rule '{}' of type {} will be ignored.",
                    ruleDefinition.getActions(),
                    ruleDefinition.getName(),
                    ruleDefinition.getCompositeRuleType());
        }
        CompositeRule compositeRule;
        String name = ruleDefinition.getName();
        switch (ruleDefinition.getCompositeRuleType()) {
            case "UnitRuleGroup":
                compositeRule = new UnitRuleGroup(name);
                break;
            case "ActivationRuleGroup":
                compositeRule = new ActivationRuleGroup(name);
                break;
            case "ConditionalRuleGroup":
                compositeRule = new ConditionalRuleGroup(name);
                break;
            default:
                throw new IllegalArgumentException("Invalid composite rule type, must be one of " + ALLOWED_COMPOSITE_RULE_TYPES);
        }
        compositeRule.setDescription(ruleDefinition.getDescription());
        compositeRule.setPriority(ruleDefinition.getPriority());

        for (RuleDefinition composingRuleDefinition : ruleDefinition.getComposingRules()) {
            compositeRule.addRule(createRule(composingRuleDefinition));
        }

        return compositeRule;
    }

这里实际上通过简单工厂模式完成了3种不同组合规则的创建,同时递归调用方法createRule完成了规则的递归构造,嵌套逻辑;

3.3 单一规则创建

不包含组合规则的条件下,调用createSimpleRule方法完成单一规则的创建,这里的具体实现包括:

SpELRuleFactory:创建SpEL表达式定义规则的工厂类

MVELRuleFactory:创建MVEL表达式定义规则的工厂类

JexlRuleFactory:创建Jexl表达式定义规则的工厂类

这里以SpELRuleFactory说明(其它类似),SpELRuleFactory源码如下:

/**
 * Factory to create {@link SpELRule} instances.
 *
 * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
 */
public class SpELRuleFactory extends AbstractRuleFactory {

    private final RuleDefinitionReader reader;
    private BeanResolver beanResolver;
    private ParserContext parserContext;

    /**
     * Create a new {@link SpELRuleFactory} with a given reader.
     *
     * @param reader used to read rule definitions
     * @see YamlRuleDefinitionReader
     * @see JsonRuleDefinitionReader
     */
    public SpELRuleFactory(RuleDefinitionReader reader) {
        this(reader, ParserContext.TEMPLATE_EXPRESSION);
    }

    /**
     * Create a new {@link SpELRuleFactory} with a given reader.
     *
     * @param reader used to read rule definitions
     * @param parserContext used to parse SpEL expressions
     * @see YamlRuleDefinitionReader
     * @see JsonRuleDefinitionReader
     */
    public SpELRuleFactory(RuleDefinitionReader reader, ParserContext parserContext) {
        this.reader = reader;
        this.parserContext = parserContext;
    }

    /**
     * Create a new {@link SpELRuleFactory} with a given reader.
     *
     * @param reader used to read rule definitions
     * @param beanResolver used to resolve bean references in SpEL expressions
     * @see YamlRuleDefinitionReader
     * @see JsonRuleDefinitionReader
     */
    public SpELRuleFactory(RuleDefinitionReader reader, BeanResolver beanResolver) {
        this.reader = reader;
        this.beanResolver = beanResolver;
    }

    /**
     * Create a new {@link SpELRuleFactory} with a given reader.
     *
     * @param reader used to read rule definitions
     * @param parserContext used to parse SpEL expressions
     * @param beanResolver used to resolve bean references in SpEL expressions
     * @see YamlRuleDefinitionReader
     * @see JsonRuleDefinitionReader
     */
    public SpELRuleFactory(RuleDefinitionReader reader, ParserContext parserContext, BeanResolver beanResolver) {
        this.reader = reader;
        this.parserContext = parserContext;
        this.beanResolver = beanResolver;
    }

    /**
     * Create a new {@link SpELRule} from a Reader.
     * 
     * The rule descriptor should contain a single rule definition.
     * If no rule definitions are found, a {@link IllegalArgumentException} will be thrown.
     * If more than a rule is defined in the descriptor, the first rule will be returned.
     *
     * @param ruleDescriptor descriptor of rule definition
     * @return a new rule
     * @throws Exception if unable to create the rule from the descriptor
     */
    public Rule createRule(Reader ruleDescriptor) throws Exception {
        List<RuleDefinition> ruleDefinitions = reader.read(ruleDescriptor);
        if (ruleDefinitions.isEmpty()) {
            throw new IllegalArgumentException("rule descriptor is empty");
        }
        return createRule(ruleDefinitions.get(0));
    }

    /**
     * Create a set of {@link SpELRule} from a Reader.
     *
     * @param rulesDescriptor descriptor of rule definitions
     * @return a set of rules
     * @throws Exception if unable to create rules from the descriptor
     */
    public Rules createRules(Reader rulesDescriptor) throws Exception {
        Rules rules = new Rules();
        List<RuleDefinition> ruleDefinitions = reader.read(rulesDescriptor);
        for (RuleDefinition ruleDefinition : ruleDefinitions) {
            rules.register(createRule(ruleDefinition));
        }
        return rules;
    }

    protected Rule createSimpleRule(RuleDefinition ruleDefinition) {
        SpELRule spELRule = new SpELRule(parserContext, beanResolver)
                .name(ruleDefinition.getName())
                .description(ruleDefinition.getDescription())
                .priority(ruleDefinition.getPriority())
                .when(ruleDefinition.getCondition());
        for (String action : ruleDefinition.getActions()) {
            spELRule.then(action);
        }
        return spELRule;
    }

}

上述createSimpleRule方法的实现中,构造了SpELRule的具体实例,完成了单一规则的创建;

至此,EasyRule框架通过工厂方法模式,完成了文件脚本规则(.json、.yml)的创建。

附工厂方法模式UML类图:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32:GPIO功能描述和工作方式

一、STM32控制原理概要 IO端口位的基本结构 在STM32有特定功能的内存单元&#xff0c;即"寄存器"。寄存器是程序与硬件电路通信的桥梁。寄存器按照每32位二进制0/1数据为一组。存储着芯片特定电路的相关信息。我们就是通过程序对寄存器中的数据进行修改&#xff0c;…

高速DSP系统设计参考指南(七)电磁干扰基础

&#xff08;七&#xff09;电磁干扰基础 1.概述2.EMI概述3.数字信号4.电流环路5.电源6.传输线7.电源层和地层8. 减少电磁干扰经验法则9.总结 1.概述 高速DSP系统中的辐射是由通过印刷电路板走线传播的快速开关电流和电压引起的。随着DSP速度的提高&#xff0c;印刷电路板走线…

【探索Linux】文件描述符 | 重定向 | 基础IO —— 强大的命令行工具 P.12

阅读导航 前言一、open()函数返回值二、文件描述符fd1. 文件描述符的分配规则2. 文件描述符0、1、2 三、重定向1. 重定向的本质⭕图解 2. dup2 系统调用函数 温馨提示 前言 前面我们讲了C语言的基础知识&#xff0c;也了解了一些数据结构&#xff0c;并且讲了有关C的一些知识&…

Python常用函数中NumPy的使用教程

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 1. txt文件 (1) 单位矩阵&#xff0c;即主对角线上的元素均为1&#xff0c;其余元素均为0的正方形矩阵。 在NumPy中可以用eye函数创建一个这样的二维数组&#…

合同管理系统

合同管理系统 功能介绍&#xff1a; 功能特性&#xff1a; 根据对合同管理系统系统分析合同管理由以下模块组成&#xff0c;相对方管理、合同文本管理、合同审批管理、合同履行审批、风险事件管理、合同查询、合同统计、系统提醒、系统管理。 1、相对方管理 1.有“相对方…

山西电力市场日前价格预测【2023-10-22】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-22&#xff09;山西电力市场全天平均日前电价为370.96元/MWh。其中&#xff0c;最高日前电价为612.26元/MWh&#xff0c;预计出现在18: 30。最低日前电价为216.57元/MWh&#xff0c;预计…

【换根DP】CF1882 D

Problem - D - Codeforces 思路&#xff1a; 一个很套路的换根 首先观察到&#xff0c;先对儿子一定比先对父亲操作来的代价小&#xff0c;因此考虑先对儿子操作&#xff0c;再对父亲操作 然后就可以直接换根了&#xff0c;首先考虑树形DP&#xff0c;设dp[u] 为 把 u 子树染…

Ubuntu系统下使用docker容器配置nginx并部署前端项目

1.下载 Nginx 镜像 命令 描述 docker pull nginx 下载最新版 Nginx 镜像 :2. 创建要挂载的宿主机目录 启动前需要先创建 Nginx 外部挂载的配置文件&#xff08; /home/nginx/conf/nginx.conf&#xff09; 之所以要先创建 , 是因为 Nginx 本身容器只存在 / etc/nginx 目录 ,…

京东数据平台:2023年9月京东净水器行业品牌销售排行榜!

鲸参谋监测的京东平台9月份净水器市场销售数据已出炉&#xff01; 根据鲸参谋平台的数据显示&#xff0c;今年9月份&#xff0c;京东平台净水器的销量为64万&#xff0c;环比下滑约9%&#xff0c;同比下滑约16%&#xff1b;销售额为5.2亿&#xff0c;环比下滑约12%&#xff0c;…

GO学习之 goroutine的调度原理

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 10、GO学习之 网络通信(Net/Htt…

springboot缓存篇之mybatis一级缓存和二级缓存

前言 相信很多人都用过mybatis&#xff0c;这篇文章主要是介绍mybatis的缓存&#xff0c;了解一下mybatis缓存是如何实现&#xff0c;以及它在实际中的应用 一级缓存 什么是mybatis一级缓存&#xff1f;我们先看一个例子&#xff1a; GetMapping("/list") public…

折半搜索-oier复健练习题目

算法介绍&#xff1a; 折半搜索常用于复杂度O(n!)级的搜索问题&#xff0c;当我们发现很显然可以将问题划分为两部分分别搜索枚举&#xff0c;再合二为一求出最终答案时&#xff0c;我们可以选择使用折半搜索。 常见数据规模&#xff1a; 对于答案的值域往往没有要求&#x…

Jenkins自动化部署SpringBoot项目的实现

本文主要介绍了Jenkins自动化部署SpringBoot项目的实现&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下 1、Jenkins介绍 1.1、概念 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成…

shell算术运算符学习笔记

文章目录 算术运算符&#xff1a;算术运算扩展算术运算指令expr算术运算指令let自增自减运算符 算术运算符&#xff1a; 加法 - 减法 * 乘法 / 除法 % 取余 ** 幂运算算术运算扩展 算术运算扩展的运算数只能是整数 [rootlocalhost tmp]# num1$[41] [rootlocalhost tmp]# echo …

子类的构造与析构过程

一、简介 父类&#xff0c;也称基类&#xff0c;其构造方法和析构方法不能被继承&#xff1b; 子类&#xff0c;也称派生类&#xff0c;继承父类的方法和属性&#xff0c;但要加入新的构造和析构函数。 二、构造与析构过程 构造&#xff1a;先调用父类——>再调用子类 析构&…

STM32 HAL高级定时器正交编码模式案例

STM32 HAL高级定时器正交编码模式案例 &#x1f516;基于stm32F030RBT6单片机采用高级定时器1&#xff0c;编码器模式&#xff0c;测试EC11编码器。 &#x1f3ac;EC11测试效果&#xff1a; &#x1f33f;STM32定时器编码器有3种映射模式: ✨本次采用的是上面的模式3&#x…

同城外卖跑腿小程序开发的关键步骤

随着快节奏的生活方式和数字化技术的不断发展&#xff0c;外卖和跑腿服务变得越来越受欢迎。为了满足这一需求&#xff0c;越来越多的创业者和企业开始着手开发同城外卖跑腿小程序。本文将详细介绍同城外卖跑腿小程序开发的关键步骤&#xff0c;帮助您了解如何成功创建一个具有…

【Cesium】绘制线、面、体

文章目录 墙矩形长方体不规则图形椭圆椭圆柱正圆圆柱圆锥不规则多边形柱一串糖葫芦&#xff1f;带洞的矩形一系列矩形、圆形贴地的线围墙宽线宽线的体复杂线状体一根水管 墙 entities.add({wall: {id:"wall",positions: Cesium.Cartesian3.fromDegreesArray([-95.0,5…

java智慧工地云平台源码,以物联网、移动互联网技术为基础,结合大数据、云计算等,实现工程管理绿色化、数字化、精细化、智能化的效果

智慧工地将更多人工智能、传感技术、虚拟现实等高科技技术植入到建筑、机械、人员穿戴设施、场地进出关口等各类物体中&#xff0c;围绕人、机、料、法、环等各方面关键因素&#xff0c;彻底改变传统建筑施工现场参建各方现场管理的交互方式、工作方式和管理模式&#xff0c;智…

结构体内存分配与编译器对齐数

一、编译器对齐数 先说一个编译器的概念&#xff1a;对齐数。 1、对于基本类型的数据成员&#xff1a; 结构体的成员的对齐数min&#xff08;编译器对齐数&#xff0c;成员占内存大小&#xff09; 例如VS编译器对齐数位8&#xff0c;那么int型数据对齐数就为4&#xff0c;doubl…