Spring SpEL 表达式语言

 一、文本表达式

文本表达式支持字符串、 数字(正数 、 实数及十六进制数) 、 布尔类型及 null。其中的字符表达式可使用单引号来表示,形如:'Deniro'。如果表达式中包含单引号或者双引号字符,那么可以使用转义字符 \。

数字支持负数 、小数、科学记数法、八进制数和十六进制数 。 默认情况下,实数使用 Double.parseDouble() 进行表达式类型转换 。

ExpressionParser parser = new SpelExpressionParser();//字符串解析, 写成“Hello World”会报错。
String str = (String) parser.parseExpression("'Hello World'").getValue();
System.out.println(str);//整型解析
int intVal = (Integer) parser.parseExpression("0x2F").getValue();
System.out.println(intVal);//双精度浮点型解析
double doubleVal = (Double) parser.parseExpression("4329759E+22").getValue();
System.out.println(doubleVal);//布尔型解析
boolean booleanVal = (boolean) parser.parseExpression("true").getValue();
System.out.println(booleanVal);

二、对象属性表达式

在 SpEL 中,我们可以使用对象属性路径(形如类名.属性名.属性名)来访问对象属性的值。

假设有一个账号类,Account.java:

public class Account {private String name;private int footballCount;private Friend friend;private List<Friend> friends;public Account(String name) {this.name = name;}public void setName(String name) {this.name = name;}public String getName() {return name;}public void setFootballCount(int footballCount) {this.footballCount = footballCount;}public int getFootballCount() {return footballCount;}public void setFriends(List<Friend> friends) {this.friends = friends;}public List<Friend> getFriends() {return friends;}public void setFriend(Friend friend) {this.friend = friend;}public Friend getFriend() {return friend;}
}

它包含姓名 name、足球数 footballCount 和一个朋友 friend 属性。friend 属性是一个 Friend 类:

public class Friend {private String name;public Friend(String name) {this.name = name;}public String getName() {return name;}
}

解析属性对象表达式

//初始化对象
Account account=new Account("Deniro");
account.setFootballCount(10);
account.addFriend(new Friend("Jack"));//解析器
ExpressionParser parser= new SpelExpressionParser();
//解析上下文
EvaluationContext context=new StandardEvaluationContext(account);//获取不同类型的属性
String name= (String) parser.parseExpression("Name").getValue(context);
System.out.println(name);
int count= (Integer) parser.parseExpression("footballCount+1").getValue(context);
System.out.println(count);//获取嵌套类中的属性
String friend= (String) parser.parseExpression("friend.name").getValue(context);
System.out.println(friend);

总结:

  • SpEL 解析器适应力强,属性名首字母大小写均可。
  • 解析对象表达式时,需要传入 EvaluationContext 上下文参数。

三、数组、List 和 Map 表达式

数组表达式支持 Java 创建数组的语法,形如 new int[]{3,4,5},数组项之间以逗号作为分隔符。

注意:目前还不支持多维数组。Map 表达式以键值对的方式来定义,形如 {name:'deniro',footballCount:10}。

//解析器
ExpressionParser parser = new SpelExpressionParser();//解析一维数组
int[] oneArray = (int[]) parser.parseExpression("new int[]{3,4,5}").getValue();
System.out.println("一维数组开始:");
for (int i : oneArray) {System.out.println(i);
}
System.out.println("一维数组结束");//这里会抛出 SpelParseException
//int[][] twoArray = (int[][]) parser.parseExpression("new int[][]{3,4,5}{3,4,5}")
//        .getValue();//解析 list
List list = (List) parser.parseExpression("{3,4,5}").getValue();
System.out.println("list:" + list);//解析 Map
Map map = (Map) parser.parseExpression("{account:'deniro',footballCount:10}").getValue();
System.out.println("map:" + map);//解析对象中的 list
final Account account = new Account("Deniro");
Friend friend1 = new Friend("Jack");
Friend friend2 = new Friend("Rose");
List<Friend> friends = new ArrayList<>();
friends.add(friend1);
friends.add(friend2);
account.setFriends(friends);
EvaluationContext context = new StandardEvaluationContext(account);
String friendName = (String) parser.parseExpression("friends[0].name").getValue(context);
System.out.println("friendName:" + friendName);

从数组与 List 获取值,可以在括号内指定索引来获取,形如上例中的 friends[0]。Map 中可通过键名来获取,形如 xxx['xxx']

四、方法表达式

SpEL 支持调用有访问权限的方法,这些方法包括对象方法、静态方法,而且支持可变方法参数。除此之外,还可以调用 String 类型中的所有可访问方法,比如 String.contains('xxx')

//解析器
ExpressionParser parser = new SpelExpressionParser();//调用 String 方法
boolean isEmpty = parser.parseExpression("'Hi,everybody'.contains('Hi')").getValue(Boolean.class);
System.out.println("isEmpty:" + isEmpty);/*** 调用对象相关方法*/
final Account account = new Account("Deniro");
EvaluationContext context = new StandardEvaluationContext(account);//调用公开方法
parser.parseExpression("setFootballCount(11)").getValue(context, Boolean.class);
System.out.println("getFootballCount:" + account.getFootballCount());//调用私有方法,抛出 SpelEvaluationException: EL1004E: Method call: Method write() cannot be found on net.deniro.spring4.spel.Account type
//parser.parseExpression("write()").getValue(context,Boolean.class);//调用静态方法
parser.parseExpression("read()").getValue(context, Boolean.class);//调用待可变参数的方法
parser.parseExpression("addFriendNames('Jack','Rose')").getValue(context, Boolean.class);

注意:调用对象的私有方法会抛出异常。

五、操作符表达式

关系操作符

SpEL 支持 Java 标准操作符:等于、不等于、小于、小等于、大于、大等于、正则表达式和 instanceof 操作符。

//解析器
ExpressionParser parser = new SpelExpressionParser();//数值比较
boolean result = parser.parseExpression("2>1").getValue(Boolean.class);
System.out.println("2>1:" + result);//字符串比较
result = parser.parseExpression("'z'>'a'").getValue(Boolean.class);
System.out.println("'z'>'a':" + result);//instanceof 运算符
result = parser.parseExpression("'str' instanceof T(String)").getValue(Boolean.class);
System.out.println("'str' 是否为字符串 :" + result);result = parser.parseExpression("1 instanceof T(Integer)").getValue(Boolean.class);
System.out.println("1 是否为整型 :" + result);//正则表达式
result = parser.parseExpression("22 matches '\\d{2}'").getValue(Boolean.class);
System.out.println("22 是否为两位数字 :" + result);

instanceof 操作符后面是类型表达式,格式为 T(Java 包装器类型),如整型 T(Integer)。注意:不能使用原生类型,如果这样 T(int) 会返回错误的判断结果。

matches 用于定义正则表达式,之后跟着单引号包裹着的正则表达式。

逻辑操作符

逻辑操作符支持以下操作:

逻辑操作符说明
and 或 &&与操作
or 或 ||或操作
!非操作

注意: 在 SpEL 中,不仅支持 Java 标准的逻辑操作符,还支持 and 与 or 关键字。

//解析器
ExpressionParser parser = new SpelExpressionParser();//与操作
boolean result = parser.parseExpression("true && true").getValue(Boolean.class);
System.out.println("与操作:" + result);//或操作
result = parser.parseExpression("true || false").getValue(Boolean.class);
System.out.println("或操作:" + result);parser.parseExpression("true or false").getValue(Boolean.class);
System.out.println("或操作(or 关键字):" + result);//非操作
result = parser.parseExpression("!false").getValue(Boolean.class);
System.out.println("非操作:" + result);//抛出 SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from java.lang.Integer to java.lang.Boolean
//parser.parseExpression("!0").getValue(Boolean.class);

注意:逻辑操作符前后运算结果必须是布尔类型,否则会抛出 SpelEvaluationException。

运算操作符

SpEL 支持 Java 运算操作符,并遵守运算符优先级规则:

运算操作符说明支持的操作数类型
+加法数字、字符串或日期
-减法数字或日期
*乘法数字
/除法数字
%取模数字
^指数幂数字
//解析器
ExpressionParser parser = new SpelExpressionParser();//加法运算
Integer iResult = parser.parseExpression("2+3").getValue(Integer.class);
System.out.println("加法运算:" + iResult);String sResult = parser.parseExpression("'Hi,'+'everybody'").getValue(String.class);
System.out.println("字符串拼接运算:" + sResult);//减法运算
iResult = parser.parseExpression("2-3").getValue(Integer.class);
System.out.println("减法运算:" + iResult);//乘法运算
iResult = parser.parseExpression("2*3").getValue(Integer.class);
System.out.println("乘法运算:" + iResult);//除法运算
iResult = parser.parseExpression("4/2").getValue(Integer.class);
System.out.println("除法运算:" + iResult);Double dResult = parser.parseExpression("4/2.5").getValue(Double.class);
System.out.println("除法运算:" + dResult);//求余运算
iResult = parser.parseExpression("5%2").getValue(Integer.class);
System.out.println("求余运算:" + iResult);

六、安全导航操作符

安全导航操作符来源于 Groovy 语言,使用它能够避免空指针异常。一般在访问对象时,需要验证该对象是否为空,使用安全导航操作符就能避免繁琐的空对象验证方法。它的格式是在获取对象属性操作符“.” 之前加一个 “?”。

final Account account = new Account("Deniro");
account.setFriend(new Friend("Jack"));//解析器
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(account);String friendName = parser.parseExpression("friend?.name").getValue(context, String.class);
System.out.println("friendName:" + friendName);//设置为 null
account.setFriend(null);
friendName = parser.parseExpression("friend?.name").getValue(context, String.class);
//打印出 null
System.out.println("friendName:" + friendName);

这里会先判断 friend 对象是否为空;如果为空,则返回 "null" 字符串;否则返回需要的属性值。

七、三元运算符

SpEL 支持标准的 Java 三元操作符:<表达式 1>?<表达式 2>:<表达式 3>

ExpressionParser parser = new SpelExpressionParser();boolean result = parser.parseExpression("(1+2) == 3?true:false").getValue(Boolean.class);
System.out.println("result:" + result);

八、Elvis 操作符

Elvis 操作符是在 Groovy 中使用的三元操作符简化版。

在三元操作符中,我们一般需要写两次变量名,比如下面代码段中的 title:

String title="News";
String actualTitle=(title!=null)?title:"tip";

使用 Elvis 操作符后,可以将上述代码段简写为:

title?:"tip"

SpEL 支持的 Elvis 操作符格式是:<var>?:<value>,如果 var 变量为 null,那就取 value 值,否则就取自身的值。所以 Elvis 操作符很适合用于设置默认值。

final Account account = new Account("Deniro");
//解析器
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(account);String friendName = parser.parseExpression("name?:'无名'").getValue(context, String.class);
System.out.println("friendName:" + friendName);//设置名字为 null
account.setName(null);
friendName = parser.parseExpression("name?:'无名'").getValue(context, String.class);
System.out.println("friendName:" + friendName);

九、赋值表达式

可以通过赋值表达式来设置属性的值,效果等同于调用 setValue() 方法。

final Account account = new Account("Deniro");//解析器
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(account);String name=parser.parseExpression("name='Jack'").getValue(context,String.class);
System.out.println("name:"+name);

十、类型操作符

类型操作符 T 可以从类路径加载指定类名称(全限定名)所对应的 Class 的实例,格式为:T(全限定类名),效果等同于 ClassLoader#loadClass()

ExpressionParser parser = new SpelExpressionParser();//加载 java.lang.Integer
Class integerClass = parser.parseExpression("T(Integer)").getValue(Class.class);
System.out.println(integerClass == java.lang.Integer.class);//加载 net.deniro.spring4.spel.Account
Class accountClass = parser.parseExpression("T(com.codedot.spel.Account)").getValue(Class.class);
System.out.println(accountClass == Account.class);//调用类静态方法
double result = (double) parser.parseExpression("T(Math).abs(-2.5)").getValue();
System.out.println("result:" + result);

我们还可以直接通过 T 操作符调用类的静态方法,格式为 T(全限定类名).静态方法名,比如上面例子中求某数的绝对值 T(Math).abs(-2.5)

SpEL 中会使用 StandardTypeLocator#findType() 方法来加载类。 findType 方法定义如下:

public Class<?> findType(String typeName) throws EvaluationException {String nameToLookup = typeName;try {return ClassUtils.forName(nameToLookup, this.classLoader);}catch (ClassNotFoundException ey) {// try any registered prefixes before giving up}for (String prefix : this.knownPackagePrefixes) {try {nameToLookup = prefix + '.' + typeName;return ClassUtils.forName(nameToLookup, this.classLoader);}catch (ClassNotFoundException ex) {// might be a different prefix}}throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName);
}
  • 尝试直接加载类。
  • 如果找不到,则尝试从已注册的包前缀(java.lang)下加载类。所以如果需要加载的类在 java.lang 下,那么可以直接写类名。
  • 如果都找不到,则抛出 SpelEvaluationException 异常。

十一、创建对象操作符

可以使用 new 操作符来创建一个新对象 。 除了基本类型(如整型、布尔型等)和字符串之外,创建其它类需要指明全限定类名( 包括包路径 ) 。

ExpressionParser parser = new SpelExpressionParser();Account account = parser.parseExpression("new com.codedot.spel.Account" + "('Deniro')").getValue(Account.class);
System.out.println("name:"+account.getName());

十二、变量表达式

可以通过 #变量名 来引用在 EvaluationContext 中定义的变量。通过 EvaluationContext#setVariable(name, val) 即可定义新的变量;name 表示变量名,val 表示变量值。

如果变量是集合,比如 list,那么可以通过 #scores.[#this] 来引用集合中的元素。

Account account = new Account("Deniro");ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(account);//定义一个新变量,名为 newVal
context.setVariable("newVal", "Jack");//获取变量 newVal 的值,并赋值给 User 的 name 属性
parser.parseExpression("name=#newVal").getValue(context);
System.out.println("getName:" + account.getName());//this 操作符表示集合中的某个元素
List<Double> scores = new ArrayList<>();
scores.addAll(Arrays.asList(23.1, 82.3, 55.9));
context.setVariable("scores", scores);//在上下文中定义 scores 变量
List<Double> scoresGreat80 = (List<Double>) parser.parseExpression("#scores.?[#this>80]").getValue(context);
System.out.println("scoresGreat80:" + scoresGreat80);

十三、集合选择表达式

可以使用选择表达式来过滤集合,从而生成一个新的符合选择条件的集合 。它的语法是 ?[selectionExpression]。选择符合条件的结果集的第一个元素的语法为 ^ [selectionExpression] ,选择最后一个元素的语法为 $[selectionExpression]。选择表达式也可应用于 Map 。

List<Integer> list = new ArrayList();
list.add(10);
list.add(21);
list.add(8);
list.add(33);ExpressionParser parser = new SpelExpressionParser();
//过滤 list 集合中的元素
StandardEvaluationContext listContext = new StandardEvaluationContext(list);
List<Integer> great4List = (List<Integer>) parser.parseExpression("?[#this>4]").getValue(listContext);
System.out.println("great4List:" + great4List);//获取匹配元素中的第一个值
Integer first = (Integer) parser.parseExpression("^[#this>2]").getValue(listContext);
System.out.println("first:" + first);//获取匹配元素中的最后一个值
Integer end = (Integer) parser.parseExpression("$[#this>2]").getValue(listContext);
System.out.println("end:" + end);

对于 List 和 Set ,是针对集合中的每一个元素进行比较的;而对于 Map,则可以指定是元素的键(key)还是元素的值进行比较的。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
//过滤 Map
Map<String, Double> rank = new HashMap<>();
rank.put("Deniro", 96.5);
rank.put("Jack", 85.3);
rank.put("Lily", 91.1);
context.setVariable("Rank", rank);//value 大于 90
Map<String, Double> rankGreat95 = (Map<String, Double>) parser.parseExpression("#Rank.?[value>90]").getValue(context);
System.out.println("rankGreat95:" + rankGreat95);//key 按字母顺序,排在 L 后面
Map<String, Double> afterL = (Map<String, Double>) parser.parseExpression("#Rank.?[key>'L']").getValue(context);
System.out.println("afterL:" + afterL);

十四、集合元素布尔判断

通过表达式 ![projectionExpression],我们可以判断集合中每一个元素是否符合表达式规则。

ExpressionParser parser = new SpelExpressionParser();
List list = (List) parser.parseExpression("{3,4,5}").getValue();
System.out.println("list:" + list);
List<Boolean> isgreat4=(List<Boolean>)parser.parseExpression("![#this>3]").getValue(list);
System.out.println("isgreat4:" + isgreat4);

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

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

相关文章

【洛谷 P1122】最大子树和 题解(深度优先搜索+树形DP)

最大子树和 题目描述 小明对数学饱有兴趣&#xff0c;并且是个勤奋好学的学生&#xff0c;总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课&#xff0c;路上见到一个老伯正在修剪花花草草&#xff0c;顿时想到了一个有关修剪花卉的问题。于是当日课后&#xff0…

ChatGPT 与 离散数学

使用ChatGPT学习离散数学是一种有效的方法&#xff0c;但请记住&#xff0c;ChatGPT只是一个文本生成模型&#xff0c;无法提供互动性的练习或答疑。下面是一些建议&#xff0c;以帮助你利用ChatGPT学习离散数学&#xff1a; 1. 明确学习目标&#xff1a;首先&#xff0c;确定…

uni-app:canvas-图形实现1

效果 代码 <template><view><!-- 创建了一个宽度为300像素&#xff0c;高度为200像素的canvas元素。canvas-id属性被设置为"firstCanvas"&#xff0c;可以用来在JavaScript中获取该canvas元素的上下文对象。 --><canvas style"width:200p…

【AI视野·今日CV 计算机视觉论文速览 第253期】Mon, 25 Sep 2023

AI视野今日CS.CV 计算机视觉论文速览 Mon, 25 Sep 2023 Totally 64 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers MosaicFusion: Diffusion Models as Data Augmenters for Large Vocabulary Instance Segmentation Authors Jiahao Xie, W…

郁金香2021年游戏辅助技术中级班(一)

郁金香2021年游戏辅助技术中级班&#xff08;一&#xff09; 用代码读取utf8名字字节数组搜索UTF-8字符串 用CE和xdbg分析对象名字从LUA函数的角度进行分析复习怪物名字偏移 用CE和xdbg分析对象数组认识虚函数表分析对象数组 分析对象数组链表部分链表的定义链表的数据在内存里…

想要精通算法和SQL的成长之路 - 最长递增子序列 II(线段树的运用)

想要精通算法和SQL的成长之路 - 最长递增子序列 II&#xff08;线段树的运用&#xff09; 前言一. 最长递增子序列 II1.1 向下递推1.2 向上递推1.3 更新操作1.4 查询操作1.5 完整代码&#xff1a; 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 最长递增子序列 II 原题链接…

智能充电桩服务系统软硬件通讯协议

该协议内容遵循modbus-rtu协议&#xff0c;如有不足&#xff0c;还请各路大神指正。 1、设备链接服务器或与服务器心跳 设备主机上电&#xff1a;设备主机自动向服务器发送 “00” 指令&#xff0c;请求注册或设备与服务器通讯心跳&#xff08;每10秒&#xff09;。 设备编号&a…

修改 Ubuntu .cache 和 pip cache 默认路径

修改 Ubuntu .cache 和 pip cache 默认路径 非常不建议修改 .cache 默认路径&#xff0c;除非你知道修改后的影响。 执行下面命令进行修改&#xff0c; vi /root/.bashrc--- 追加 export XDG_CACHE_HOME/u01/.cache export PIP_CACHE_DIR/u01/.cache ---完结&#xff01;

vue3使用pinia 实现权限code存取

文章目录 前言一、pinia 简要认识二、实现思路三、详细实现步骤1.用pinia 定义user store 用来存储用户相关的数据,安装 **js-cookie **来辅助存在cookie里2. 在登录后获取用户的userId 与 token 后&#xff0c;调用定义好的 getUserPermisson 获取用户的权限code&#xff0c;并…

OpenAI官方吴达恩《ChatGPT Prompt Engineering 提示词工程师》(2)如何迭代开发提示词

迭代/Iterative 在机器学习中&#xff0c;您经常有一个想法&#xff0c;然后实现它。编写代码&#xff0c;获取数据&#xff0c;训练模型&#xff0c;这就给您一个实验结果。然后您可以查看该输出&#xff0c;进行错误分析&#xff0c;找出哪些地方工作或不工作&#xff0c;然后…

VMware/kali使用小贴士(持续更新(吧

关于截屏 kali截屏快捷键 关于虚拟机和主机切屏 因为kali不全屏看还挺难受的&#xff0c;全屏了又很难切回主机 虚拟机全屏时&#xff0c;可以先Alt Ctrl取消对VM的控制&#xff08;光标移到上方白条也是同理&#xff09;&#xff0c;然后Alt Tab切换窗口

郁金香2021年游戏辅助技术(初级班)(上)

郁金香2021年游戏辅助技术初级班&#xff08;上&#xff09; %p、size_t、%zd、%llu、FindWindow、GetWindowText、SetWindowTextGetWindowThreadProcessId、OpenProcess、ReadProcessMemory封接读内存接口函数 int R4(void* 地址)跨进程向目标进程内存地址写入数值 WriteProce…

【每日一题】递枕头

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;模拟方法二&#xff1a; O ( 1 ) O(1) O(1) 解法 写在最后 Tag 【模拟】【 O ( 1 ) O(1) O(1) 公式】【2023-09-26】 题目来源 2582. 递枕头 题目解读 编号从 1 到 n 的 n 个人站成一排传递枕头。最初&#xff0c;排…

华南理工大学电子与信息学院23年预推免复试面试经验贴

运气较好&#xff0c;复试分数90.24&#xff0c;电科学硕分数线84、信通83、专硕电子与信息74. 面试流程&#xff1a; 1&#xff1a;5min ppt的介绍。其中前2min用英语简要介绍基本信息&#xff0c;后3min可用英语也可用中文 介绍具体项目信息如大创、科研、竞赛等&#xff08…

出现 conda虚拟环境默认放在C盘 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法3.1 方法一3.2 方法二1. 问题所示 通过conda配置虚拟环境的时候,由于安装在D盘下,但是配置的环境默认都给我放C盘 通过如下命令:conda env list,最后查看该环境的确在C盘下 2. 原理分析 究其根本原因,这是因为默认路径没有足够的…

编程每日一练(多语言实现):判断偶数

文章目录 一、实例描述二、技术要点三、代码实现3.1 C 语言实现3.2 Python 语言实现3.3 Java 语言实现 一、实例描述 利用单条件单分支选择语句判断输入的一个整数 是否是偶数。 运行程序&#xff0c;输入一个 整数18&#xff0c; 然后按回车键&#xff0c;将提示该数字是偶数…

linux使用操作[2]

文章目录 版权声明网络传输ping命令wget命令curl命令端口linux端口端口命令和工具 进程管理查看进程关闭进程 主机状态top命令内容详解磁盘信息监控 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明&#xff0c;所有版权属于黑马程序员或相…

Android ConstraintLayout app:layout_constraintHorizontal_weight

Android ConstraintLayout app:layout_constraintHorizontal_weight <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:…

【已解决】qt死活不响应鼠标移动到按钮事件

本博文源于笔者正在研究的内容&#xff0c;这个问题大概捣鼓了一个下午&#xff0c;问题是这样子&#xff1a;我有一个按钮&#xff0c;我应用程序运行时&#xff0c;我鼠标放到按钮上&#xff0c;按钮就会被填充图标。怀揣着这样一个想法&#xff0c;我搜啊搜&#xff0c;整啊…

verilog学习笔记(1)module实例化2

移位寄存器多路选择器 我的代码&#xff1a; module top_module ( input clk, input [7:0] d, input [1:0] sel, output [7:0] q );wire [7:0] w1;wire [7:0] w2;wire [7:0] w3;my_dff8 my_dff8_1(.clk(clk),.d(d),.q(w1));my_dff8 my_dff8_2(.clk(clk),.d(w1),.q(w2));my_d…