EasyRule源码:EasyRule框架源码分析

2023-10-19 21:52
文章标签 分析 源码 框架 easyrule

本文主要是介绍EasyRule源码:EasyRule框架源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.简要介绍EasyRule

2.从实例入手看EasyRule使用方法

3.执行过程源码分析

3.1 Fact&Facts

3.2 规则定义和注册

3.2.1 Rule接口

3.2.2 规则注册管理

3.2.3 创建规则代理

3.3 规则引擎调度执行


1.简要介绍EasyRule

当下的规则引擎选择非常多,例如 EasyRule、Aviator 、QLExpress、Drools、Zools等;前面的文章也重点分析了Aviator框架源码,相关文章见:

1.【精选】Aviator源码:从具体实例看Aviator属性语法糖源码分析(a.b.c)_aviator 变量语法糖_mumubili的博客-CSDN博客

2. Aviator源码:Aviator表达式引擎执行过程源码分析_表达式引擎aviator-CSDN博客
 

本篇对另一种常用的规则引擎框架EasyRule进行源码分析;

EasyRule作为一种小而美的轻量级的规则引擎开源框架,从实际业务规则应用场景出发,抽象出了条件(Condition)、动作(Action)、规则(Rule)数据模型,并通过规则引擎(RulesEngine)完成规则的调度执行。

EasyRule的主要特性归纳如下:

  • 轻量级框架和易于学习的API

  • 基于POJO的开发与注解的编程模型

  • 定义抽象的业务规则并轻松应用它们

  • 支持从简单规则创建组合规则的能力

  • 支持使用表达式语言(如MVEL和SpEL)定义规则的能力

本文对EasyRule归纳性的描述不再过多赘述,下面主要着重对EasyRule框架源码进行分析;

2.从实例入手看EasyRule使用方法

EasyRule源码大家可以在github上找到:Github之EasyRule

这里选取源码中Tutorial模块中的例子进行说明:

@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {@Conditionpublic boolean itRains(@Fact("rain") boolean rain) {return rain;}@Actionpublic void takeAnUmbrella() {System.out.println("It rains, take an umbrella!");}
}

这里通过注解模式对业务规则进行定义;

  1. @Rule:表明该类为一个规则类
  2. @Condition:表明规则类中的方法作为规则条件
  3. @Fact:作为条件中的传参
  4. @Action:作为规则条件命中之后具体执行的动作

完成规则类的定义后,通过启动测试验证类,验证规则的执行情况:

public class Launcher {public static void main(String[] args) {// define factsFacts facts = new Facts();facts.put("rain", true);// define rulesWeatherRule weatherRule = new WeatherRule();Rules rules = new Rules();rules.register(weatherRule);// fire rules on known factsRulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);}}

3.执行过程源码分析

上述的启动类中,只要包含3部分:

1)定义并初始化Facts

2)定义并注册规则

3)定义规则引擎,并进行规则调度

下面分别对这3部分进行分析

3.1 Fact&Facts

Fact&Facts源码如下:

public class Fact<T> {private final String name;private final T value;/*** Create a new fact.* @param name of the fact* @param value of the fact*/public Fact(String name, T value) {Objects.requireNonNull(name, "name must not be null");Objects.requireNonNull(value, "value must not be null");this.name = name;this.value = value;}/*** Get the fact name.* @return fact name*/public String getName() {return name;}/*** Get the fact value.* @return fact value*/public T getValue() {return value;}@Overridepublic String toString() {return "Fact{" +"name='" + name + '\'' +", value=" + value +'}';}/** The Facts API represents a namespace for facts where each fact has a unique name.* Hence, equals/hashcode are deliberately calculated only on the fact name.*/@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Fact<?> fact = (Fact<?>) o;return name.equals(fact.name);}@Overridepublic int hashCode() {return Objects.hash(name);}
}
/*** This class encapsulates a set of facts and represents a facts namespace.* Facts have unique names within a <code>Facts</code> object.** @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)*/
public class Facts implements Iterable<Fact<?>> {private final Set<Fact<?>> facts = new HashSet<>();/*** Add a fact, replacing any fact with the same name.** @param name of the fact to add, must not be null* @param value of the fact to add, must not be null*/public <T> void put(String name, T value) {Objects.requireNonNull(name, "fact name must not be null");Objects.requireNonNull(value, "fact value must not be null");Fact<?> retrievedFact = getFact(name);if (retrievedFact != null) {remove(retrievedFact);}add(new Fact<>(name, value));}/*** Add a fact, replacing any fact with the same name.* * @param fact to add, must not be null*/public <T> void add(Fact<T> fact) {Objects.requireNonNull(fact, "fact must not be null");Fact<?> retrievedFact = getFact(fact.getName());if (retrievedFact != null) {remove(retrievedFact);}facts.add(fact);}/*** Remove a fact by name.** @param factName name of the fact to remove, must not be null*/public void remove(String factName) {Objects.requireNonNull(factName, "fact name must not be null");Fact<?> fact = getFact(factName);if (fact != null) {remove(fact);}}/*** Remove a fact.** @param fact to remove, must not be null*/public <T> void remove(Fact<T> fact) {Objects.requireNonNull(fact, "fact must not be null");facts.remove(fact);}/*** Get the value of a fact by its name. This is a convenience method provided* as a short version of {@code getFact(factName).getValue()}.** @param factName name of the fact, must not be null* @param <T> type of the fact's value* @return the value of the fact having the given name, or null if there is* no fact with the given name*/@SuppressWarnings("unchecked")public <T> T get(String factName) {Objects.requireNonNull(factName, "fact name must not be null");Fact<?> fact = getFact(factName);if (fact != null) {return (T) fact.getValue();}return null;}/*** Get a fact by name.** @param factName name of the fact, must not be null* @return the fact having the given name, or null if there is no fact with the given name*/public Fact<?> getFact(String factName) {Objects.requireNonNull(factName, "fact name must not be null");return facts.stream().filter(fact -> fact.getName().equals(factName)).findFirst().orElse(null);}/*** Return a copy of the facts as a map. It is not intended to manipulate* facts outside of the rules engine (aka other than manipulating them through rules).** @return a copy of the current facts as a {@link HashMap}*/public Map<String, Object> asMap() {Map<String, Object> map = new HashMap<>();for (Fact<?> fact : facts) {map.put(fact.getName(), fact.getValue());}return map;}/*** Return an iterator on the set of facts. It is not intended to remove* facts using this iterator outside of the rules engine (aka other than doing it through rules)* * @return an iterator on the set of facts*/@Overridepublic Iterator<Fact<?>> iterator() {return facts.iterator();}/*** Clear facts.*/public void clear() {facts.clear();}@Overridepublic String toString() {Iterator<Fact<?>> iterator = facts.iterator();StringBuilder stringBuilder = new StringBuilder("[");while (iterator.hasNext()) {stringBuilder.append(iterator.next().toString());if (iterator.hasNext()) {stringBuilder.append(",");}}stringBuilder.append("]");return stringBuilder.toString();}
}

Fact:封装了对象名称到实际对象引用的映射

Facts:对Fact进一步封装为Set集合,以及对应的集合操作

3.2 规则定义和注册

3.2.1 Rule接口

规则Rule整体类图如下:

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

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

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

CompositeRule:聚合规则,多个规则聚合管理

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

Rules:负责规则的注册管理,其中完成了规则的代理过程

Rule接口定义如下,主要包含了规则的名称、描述、优先级等属性定义:

/*** Abstraction for a rule that can be fired by a rules engine.** Rules are registered in a namespace of rule of type {@link Rules}* in which they must have a <strong>unique</strong> name.** @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)*/
public interface Rule extends Comparable<Rule> {/*** Default rule name.*/String DEFAULT_NAME = "rule";/*** Default rule description.*/String DEFAULT_DESCRIPTION = "description";/*** Default rule priority.*/int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;/*** Getter for rule name.* @return the rule name*/default String getName() {return DEFAULT_NAME;}/*** Getter for rule description.* @return rule description*/default String getDescription() {return DEFAULT_DESCRIPTION;}/*** Getter for rule priority.* @return rule priority*/default int getPriority() {return DEFAULT_PRIORITY;}/*** This method implements the rule's condition(s).* <strong>Implementations should handle any runtime exception and return true/false accordingly</strong>** @return true if the rule should be applied given the provided facts, false otherwise*/boolean evaluate(Facts facts);/*** This method implements the rule's action(s).* @throws Exception thrown if an exception occurs when performing action(s)*/void execute(Facts facts) throws Exception;}

3.2.2 规则注册管理

对规则的注册是通过Rules类完成的,通过调用register和unregister方法完成规则的注册管理;

/*** This class encapsulates a set of rules and represents a rules namespace.* Rules must have a unique name within a rules namespace.* * Rules will be compared to each other based on {@link Rule#compareTo(Object)}* method, so {@link Rule}'s implementations are expected to correctly implement* {@code compareTo} to ensure unique rule names within a single namespace.** @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)*/
public class Rules implements Iterable<Rule> {private Set<Rule> rules = new TreeSet<>();/*** Create a new {@link Rules} object.** @param rules to register*/public Rules(Set<Rule> rules) {this.rules = new TreeSet<>(rules);}/*** Create a new {@link Rules} object.** @param rules to register*/public Rules(Rule... rules) {Collections.addAll(this.rules, rules);}/*** Create a new {@link Rules} object.** @param rules to register*/public Rules(Object... rules) {this.register(rules);}/*** Register one or more new rules.** @param rules to register, must not be null*/public void register(Object... rules) {Objects.requireNonNull(rules);for (Object rule : rules) {Objects.requireNonNull(rule);this.rules.add(RuleProxy.asRule(rule));}}/*** Unregister one or more rules.** @param rules to unregister, must not be null*/public void unregister(Object... rules) {Objects.requireNonNull(rules);for (Object rule : rules) {Objects.requireNonNull(rule);this.rules.remove(RuleProxy.asRule(rule));}}/*** Unregister a rule by name.** @param ruleName name of the rule to unregister, must not be null*/public void unregister(final String ruleName) {Objects.requireNonNull(ruleName);Rule rule = findRuleByName(ruleName);if (rule != null) {unregister(rule);}}/*** Check if the rule set is empty.** @return true if the rule set is empty, false otherwise*/public boolean isEmpty() {return rules.isEmpty();}/*** Clear rules.*/public void clear() {rules.clear();}/*** Return how many rules are currently registered.** @return the number of rules currently registered*/public int size() {return rules.size();}/*** Return an iterator on the rules set. It is not intended to remove rules* using this iterator.* @return an iterator on the rules set*/@Overridepublic Iterator<Rule> iterator() {return rules.iterator();}private Rule findRuleByName(String ruleName) {return rules.stream().filter(rule -> rule.getName().equalsIgnoreCase(ruleName)).findFirst().orElse(null);}
}

3.2.3 创建规则代理

在上面规则注册的方法中,通过调用RuleProxy.asRule(rule)完成规则的代理,实际注册的是规则的代理类,下面剖析下代理类的构造过程:

/*** Main class to create rule proxies from annotated objects.** @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)*/
public class RuleProxy implements InvocationHandler {private final Object target;private String name;private String description;private Integer priority;private Method[] methods;private Method conditionMethod;private Set<ActionMethodOrderBean> actionMethods;private Method compareToMethod;private Method toStringMethod;private org.jeasy.rules.annotation.Rule annotation;private static final RuleDefinitionValidator ruleDefinitionValidator = new RuleDefinitionValidator();private static final Logger LOGGER = LoggerFactory.getLogger(RuleProxy.class);/*** Makes the rule object implement the {@link Rule} interface.** @param rule the annotated rule object.* @return a proxy that implements the {@link Rule} interface.*/public static Rule asRule(final Object rule) {Rule result;if (rule instanceof Rule) {result = (Rule) rule;} else {ruleDefinitionValidator.validateRuleDefinition(rule);result = (Rule) Proxy.newProxyInstance(Rule.class.getClassLoader(),new Class[]{Rule.class, Comparable.class},new RuleProxy(rule));}return result;}private RuleProxy(final Object target) {this.target = target;}@Overridepublic Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {String methodName = method.getName();switch (methodName) {case "getName":return getRuleName();case "getDescription":return getRuleDescription();case "getPriority":return getRulePriority();case "compareTo":return compareToMethod(args);case "evaluate":return evaluateMethod(args);case "execute":return executeMethod(args);case "equals":return equalsMethod(args);case "hashCode":return hashCodeMethod();case "toString":return toStringMethod();default:return null;}}private Object evaluateMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {Facts facts = (Facts) args[0];Method conditionMethod = getConditionMethod();try {List<Object> actualParameters = getActualParameters(conditionMethod, facts);return conditionMethod.invoke(target, actualParameters.toArray()); // validated upfront} catch (NoSuchFactException e) {LOGGER.warn("Rule '{}' has been evaluated to false due to a declared but missing fact '{}' in {}",getTargetClass().getName(), e.getMissingFact(), facts);return false;} catch (IllegalArgumentException e) {LOGGER.warn("Types of injected facts in method '{}' in rule '{}' do not match parameters types",conditionMethod.getName(), getTargetClass().getName(), e);return false;}}private Object executeMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {Facts facts = (Facts) args[0];for (ActionMethodOrderBean actionMethodBean : getActionMethodBeans()) {Method actionMethod = actionMethodBean.getMethod();List<Object> actualParameters = getActualParameters(actionMethod, facts);actionMethod.invoke(target, actualParameters.toArray());}return null;}private Object compareToMethod(final Object[] args) throws Exception {Method compareToMethod = getCompareToMethod();Object otherRule = args[0]; // validated upfrontif (compareToMethod != null && Proxy.isProxyClass(otherRule.getClass())) {if (compareToMethod.getParameters().length != 1) {throw new IllegalArgumentException("compareTo method must have a single argument");}RuleProxy ruleProxy = (RuleProxy) Proxy.getInvocationHandler(otherRule);return compareToMethod.invoke(target, ruleProxy.getTarget());} else {return compareTo((Rule) otherRule);}}private List<Object> getActualParameters(Method method, Facts facts) {List<Object> actualParameters = new ArrayList<>();Annotation[][] parameterAnnotations = method.getParameterAnnotations();for (Annotation[] annotations : parameterAnnotations) {if (annotations.length == 1) {String factName = ((Fact) (annotations[0])).value(); //validated upfront.Object fact = facts.get(factName);if (fact == null && !facts.asMap().containsKey(factName)) {throw new NoSuchFactException(format("No fact named '%s' found in known facts: %n%s", factName, facts), factName);}actualParameters.add(fact);} else {actualParameters.add(facts); //validated upfront, there may be only one parameter not annotated and which is of type Facts.class}}return actualParameters;}private boolean equalsMethod(final Object[] args) throws Exception {if (!(args[0] instanceof Rule)) {return false;}Rule otherRule = (Rule) args[0];int otherPriority = otherRule.getPriority();int priority = getRulePriority();if (priority != otherPriority) {return false;}String otherName = otherRule.getName();String name = getRuleName();if (!name.equals(otherName)) {return false;}String otherDescription = otherRule.getDescription();String description =  getRuleDescription();return Objects.equals(description, otherDescription);}private int hashCodeMethod() throws Exception {int result   = getRuleName().hashCode();int priority = getRulePriority();String description = getRuleDescription();result = 31 * result + (description != null ? description.hashCode() : 0);result = 31 * result + priority;return result;}private Method getToStringMethod() {if (this.toStringMethod == null) {Method[] methods = getMethods();for (Method method : methods) {if ("toString".equals(method.getName())) {this.toStringMethod = method;return this.toStringMethod;}}}return this.toStringMethod;}private String toStringMethod() throws Exception {Method toStringMethod = getToStringMethod();if (toStringMethod != null) {return (String) toStringMethod.invoke(target);} else {return getRuleName();}}private int compareTo(final Rule otherRule) throws Exception {int otherPriority = otherRule.getPriority();int priority = getRulePriority();if (priority < otherPriority) {return -1;} else if (priority > otherPriority) {return 1;} else {String otherName = otherRule.getName();String name = getRuleName();return name.compareTo(otherName);}}private int getRulePriority() throws Exception {if (this.priority == null) {int priority = Rule.DEFAULT_PRIORITY;org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();if (rule.priority() != Rule.DEFAULT_PRIORITY) {priority = rule.priority();}Method[] methods = getMethods();for (Method method : methods) {if (method.isAnnotationPresent(Priority.class)) {priority = (int) method.invoke(target);break;}}this.priority = priority;}return this.priority;}private Method getConditionMethod() {if (this.conditionMethod == null) {Method[] methods = getMethods();for (Method method : methods) {if (method.isAnnotationPresent(Condition.class)) {this.conditionMethod = method;return this.conditionMethod;}}}return this.conditionMethod;}private Set<ActionMethodOrderBean> getActionMethodBeans() {if (this.actionMethods == null) {this.actionMethods = new TreeSet<>();Method[] methods = getMethods();for (Method method : methods) {if (method.isAnnotationPresent(Action.class)) {Action actionAnnotation = method.getAnnotation(Action.class);int order = actionAnnotation.order();this.actionMethods.add(new ActionMethodOrderBean(method, order));}}}return this.actionMethods;}private Method getCompareToMethod() {if (this.compareToMethod == null) {Method[] methods = getMethods();for (Method method : methods) {if (method.getName().equals("compareTo")) {this.compareToMethod = method;return this.compareToMethod;}}}return this.compareToMethod;}private Method[] getMethods() {if (this.methods == null) {this.methods = getTargetClass().getMethods();}return this.methods;}private org.jeasy.rules.annotation.Rule getRuleAnnotation() {if (this.annotation == null) {this.annotation = Utils.findAnnotation(org.jeasy.rules.annotation.Rule.class, getTargetClass());}return this.annotation;}private String getRuleName() {if (this.name == null) {org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();this.name = rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name();}return this.name;}private String getRuleDescription() {if (this.description == null) {// Default description = "when " + conditionMethodName + " then " + comma separated actionMethodsNamesStringBuilder description = new StringBuilder();appendConditionMethodName(description);appendActionMethodsNames(description);org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();this.description = rule.description().equals(Rule.DEFAULT_DESCRIPTION) ? description.toString() : rule.description();}return this.description;}private void appendConditionMethodName(StringBuilder description) {Method method = getConditionMethod();if (method != null) {description.append("when ");description.append(method.getName());description.append(" then ");}}private void appendActionMethodsNames(StringBuilder description) {Iterator<ActionMethodOrderBean> iterator = getActionMethodBeans().iterator();while (iterator.hasNext()) {description.append(iterator.next().getMethod().getName());if (iterator.hasNext()) {description.append(",");}}}public Object getTarget() {return target;}private Class<?> getTargetClass() {return target.getClass();}}

通过实现InvocationHandler完成Interface-based JDK的动态代理过程,且在代理类中,对规则类的注解@Rule、@Condition、@Action、@Fact进行了解析并加以缓存;

3.3 规则引擎调度执行

规则引擎整体类图:

RulesEngine:定义顶层规则引擎接口

AbstractRulesEngine:抽象规则引擎实现类,封装规则拦截器和规则引擎拦截器注册管理逻辑

DefaultRulesEngine:默认规则引擎实现类

InferenceRulesEngine:委托规则引擎实现类,支持条件命中的条件下,多次触发规则执行

RulesEngineParameters:封装规则引擎配置参数

RuleListener:规则执行拦截器器

RulesEngineListener:规则引擎执行拦截器

 RulesEngine接口定义如下:

/*** Rules engine interface.** @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)*/
public interface RulesEngine {/*** Return the rules engine parameters.** @return The rules engine parameters*/RulesEngineParameters getParameters();/*** Return the list of registered rule listeners.** @return the list of registered rule listeners*/default List<RuleListener> getRuleListeners() {return Collections.emptyList();}/*** Return the list of registered rules engine listeners.** @return the list of registered rules engine listeners*/default List<RulesEngineListener> getRulesEngineListeners() {return Collections.emptyList();}/*** Fire all registered rules on given facts.*/void fire(Rules rules, Facts facts);/*** Check rules without firing them.* @return a map with the result of evaluation of each rule*/default Map<Rule, Boolean> check(Rules rules, Facts facts) {return Collections.emptyMap();}
}

下面重点分析下fire方法的执行过程:

    @Overridepublic void fire(Rules rules, Facts facts) {Objects.requireNonNull(rules, "Rules must not be null");Objects.requireNonNull(facts, "Facts must not be null");triggerListenersBeforeRules(rules, facts);doFire(rules, facts);triggerListenersAfterRules(rules, facts);}void doFire(Rules rules, Facts facts) {if (rules.isEmpty()) {LOGGER.warn("No rules registered! Nothing to apply");return;}logEngineParameters();log(rules);log(facts);LOGGER.debug("Rules evaluation started");for (Rule rule : rules) {final String name = rule.getName();final int priority = rule.getPriority();if (priority > parameters.getPriorityThreshold()) {LOGGER.debug("Rule priority threshold ({}) exceeded at rule '{}' with priority={}, next rules will be skipped",parameters.getPriorityThreshold(), name, priority);break;}if (!shouldBeEvaluated(rule, facts)) {LOGGER.debug("Rule '{}' has been skipped before being evaluated", name);continue;}boolean evaluationResult = false;try {evaluationResult = rule.evaluate(facts);} catch (RuntimeException exception) {LOGGER.error("Rule '" + name + "' evaluated with error", exception);triggerListenersOnEvaluationError(rule, facts, exception);// give the option to either skip next rules on evaluation error or continue by considering the evaluation error as falseif (parameters.isSkipOnFirstNonTriggeredRule()) {LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");break;}}if (evaluationResult) {LOGGER.debug("Rule '{}' triggered", name);triggerListenersAfterEvaluate(rule, facts, true);try {triggerListenersBeforeExecute(rule, facts);rule.execute(facts);LOGGER.debug("Rule '{}' performed successfully", name);triggerListenersOnSuccess(rule, facts);if (parameters.isSkipOnFirstAppliedRule()) {LOGGER.debug("Next rules will be skipped since parameter skipOnFirstAppliedRule is set");break;}} catch (Exception exception) {LOGGER.error("Rule '" + name + "' performed with error", exception);triggerListenersOnFailure(rule, exception, facts);if (parameters.isSkipOnFirstFailedRule()) {LOGGER.debug("Next rules will be skipped since parameter skipOnFirstFailedRule is set");break;}}} else {LOGGER.debug("Rule '{}' has been evaluated to false, it has not been executed", name);triggerListenersAfterEvaluate(rule, facts, false);if (parameters.isSkipOnFirstNonTriggeredRule()) {LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");break;}}}}

如上fire方法主要的执行步骤为:

1.在doFire方法执行前后,调用了规则引擎拦截器的逻辑

2.在doFire中,遍历执行所有注册的规则

3.在规则执行的前后,也横切了规则拦截器的拦截逻辑

这篇关于EasyRule源码:EasyRule框架源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/242607

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud