-
freemarker如何得到map的某个值
2017-02-17 15:46:06有两种情况: 1.map的key已知为某字符串常量,要查对应value。 ${item["total"]} ...想要得到map.get("total")同效果的值,使用${item...2.map的key需要进行动态获取,以查对应value。 ${item_index + 1} ${it有两种情况:
1.map的key已知为某字符串常量,要查对应value。
<#list list as item >
${item["total"]}
</#list>例子中,item是一个List<Map>一个map对象。
想要得到map.get("total")同效果的值,使用${item["total"]}。
2.map的key需要进行动态获取,以查对应value。
<#list list as item>
<tr>
<td>${item_index + 1}</td>
<td style="text-align: left">${item["title"]}</td>
<#list zyList as key>
<td rowspan="1">${item[key]}</td>
</#list>
<td>${item["total"]}</td>
</tr>
</#list>例子中的list与item同上。
zyList是一个List<String>的实例对象。
key为zyList某次得到的某个String对象。
key的值在是动态得到的。
因此使用${item[key]}来获得。
-
用strust标签如何获取Map 具体某个键的值,就像map.get(key)
2013-07-03 20:57:37用strust标签如何获取Map 具体某个键的值,就像map.get(key) -
es6数组里面获取某个值对_ES6 中的 Set 和 Map 到底是什么?
2021-01-01 16:57:37我们今天就从最基本的角度来学习 Map 和 Set 是如何使用的,我们在什么场景中会使用到这两种数据结构,以及和原本的对象和数组数据结构有什么不一样的地方。阐述 Map 数据结构Map 类似于对象,但普通对象的 key 必须...
作者 | 深夜改bug策划 | 前端三省
ES6中新增了两种数据类型,就是 Map 和 Set 这两种数据结构。我们今天就从最基本的角度来学习 Map 和 Set 是如何使用的,我们在什么场景中会使用到这两种数据结构,以及和原本的对象和数组数据结构有什么不一样的地方。
阐述 Map 数据结构
Map 类似于对象,但普通对象的 key 必须是字符串或者数字,而 Map 的 key 可以是任何数据类型。
下面我们来看下如何创建 键(key)不是字符串或者数值的 Map 对象。
const mapObj = new Map()
const obj = {
name: 'aven'
}
mapObj.set(obj, 'Yes, you are right!')
console.log(mapObj) // Map(1) {{…} => "Yes, you are right!"}我们在创建 Map 对象的时候,通过 new 关键字来创建一个 map 实例。同时我们调用 map 实例可以访问到的
set(key, val)
方法来为 map 实例对象添加属性。Map 实例的属性
size:获取键值对的数量
set:设置键值对的 key 和 value
get:获取键值对的属性值
has:判断键值对是否存在
delete:删除键值对
clear:清空所有键值对
这里的键值对,我们可以理解为 Map 成员。
下面贴一段代码,方便大家理解。
const mapObj = new Map()
mapObj.set('name', 'junior')
mapObj.get('name') // junior
mapObj.size // 1
mapObj.has('name') // true
mapObj.delete('name') // 和 js 中的 delete 用法一致
mapObj.clear() // 清空 Map 实例上的所有成员Map 实例上拥有 iterator 接口可用 for of 遍历的方法
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。
const map = new Map()
map.set('name', 'junior')
下面贴一段代码,方便大家理解。
const mapObj = new Map()
mapObj.set('name', 'aven')
mapObj.set('sex', '男')
for (let key of mapObj.keys()) {
// name
// sex
console.log(key)
}
for (let value of mapObj.values()) {
// aven
// 男
console.log(value)
}
for (let item of mapObj.entries()) {
console.log(item[0], item[1]) // aven 男
}Map 数据结构使用场景
大家需要注意的一点是 Map 里面的成员是有序的,Object 里面的键值对是无序的,这是容易忽略的一点。
Map 数据结构的实例可以使用
for of
来遍历,但是 Object 类型的不可以,因为没有提供iterator
接口。Map 可以用任何类型作为键。明确保留顺序。同时避免与 Object 优化场景冲突,性能优化潜力更大。
阐述 Set 数据结构
Set 类似一个数组,但是其中的值都是唯一的,Set 本身是一个构造函数,用来生成 Set 数据结构。
先来看一下我们如何创建一个 Set 结构的实例
const setArr = new Set([1, 2, 3, 3, 4, 5, 1, 5])
console.log(setArr) // Set(5) {1, 2, 3, 4, 5}也可以接受伪数组作为参数来生成 Set 结构
const set = new Set(document.querySelectorAll('div'))
set.size // 输出结果:Set 成员的数量Set 结构的实例属性
size:获取成员的数量
add:添加某个值,返回 Set 结构本身
delete:删除某个值,返回一个布尔值,表示删除是否成功
has:返回一个布尔值,表示该值是否为Set的成员
clear:清除所有成员,没有返回值
看下面的代码,有助于理解
s.add(1).add(2).add(2) // 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(3) // false
s.delete(2)
s.has(2) // falseSet 实例上拥有 iterator 接口可用 for of 遍历的方法
keys():返回键名的遍历器
vaules():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
看下面的代码,有助于理解
let set = new Set(['red', 'green', 'blue'])
for (let item of set.keys()) {
console.log(item)
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item)
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item)
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]Set 数据结构使用场景
数组去重
let arr = [1, 1, 2, 3]
let unique = [... new Set(arr)]数组并集
let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])
let union = [...new Set([...a, ...b])]数组交集
let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])
let intersect = [...new Set([...a].filter(x => b.has(x)))]数组差集
let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])
let difference = Array.from(new Set([...a].filter(x => !b.has(x))))
对 Set 数据结构的另外一种理解
Set 可以轻易达到去重的目的。但更彻底的理解方式是,Set 是只有键的 Map,而不是不重复的 Array。
因此它可以理解为 Map 的语法糖,它比较的对象依然是 Object,而不是 Array。
因此它真正的优点同样是允许用任何类型的键、保留顺序,而不是去重。
这些用 Array 都能手动模拟,但问题是性能上会存在很大的问题,同时也很不方便。
如果这些都不重要,那其实 ES 连 Array 都不必有了,因为用 Object 也能模拟 Array。
从图灵完备的角度来说,甚至 string 都不必有了,因为它们都能用用 Object 模拟的 Array 配合 number 实现。
推荐关注
-
前端如何获取后台通过map封装的值_【怎样防止代码被抄袭】浅谈前端代码加密...
2020-12-05 03:18:21而在这个专业领域中,作为开发者我们众所周知的是,所有来自前端的数据都是“不可信”的,由于构成前端业务逻辑和交互界面的所有相关代码都是可以被用户直接查看到的,因此我们无法保证我们所确信的某个从前端传递到...说到 Web 前端开发,我们首先能够想到的是浏览器、HTML、CSS 以及 JavaScript 这些开发时所必备使用的软件工具和编程语言。而在这个专业领域中,作为开发者我们众所周知的是,所有来自前端的数据都是“不可信”的,由于构成前端业务逻辑和交互界面的所有相关代码都是可以被用户直接查看到的,因此我们无法保证我们所确信的某个从前端传递到后端的数据没有被用户曾经修改过。那么是否有办法可以将前端领域中那些与业务有关的代码(比如数据处理逻辑、验证逻辑等。通常是 JavaScript 代码)进行加密以防止用户进行恶意修改呢?本文我们将讨论这方面的内容。
提到“加密”,我们自然会想到众多与“对称加密”、“非对称加密”以及“散列加密”相关的算法,比如 AWS 算法、RSA 算法与 MD5 算法等。在传统的 B-S 架构下,前端通过公钥进行加密处理的数据可以在后端服务器再通过相应私钥进行解密来得到原始数据,但是对于前端的业务代码而言,由于浏览器本身无法识别运行这些被加密过的源代码,因此实际上传统的加密算法并不能帮助我们解决“如何完全黑盒化前端业务逻辑代码”这一问题。既然无法完全隐藏前端业务逻辑代码的实际执行细节,那我们就从另一条路以“降低代码可读性”的方式来“伪黑盒化前端业务逻辑代码”。通常的方法有如下几种:
第三方插件
我们所熟知的可用在 Web 前端开发中的第三方插件主要有:Adobe Flash、Java Applet 以及 Silverlight 等。由于历史原因这里我们不会深入介绍基于这些第三方插件的前端业务代码加密方案。其中 Adobe 将于2020年完全停止对 Flash 技术的支持,Chrome、Edge 等浏览器也开始逐渐对使用了 Flash 程序的 Web 页面进行阻止或弹出相应的警告。同样的,来自微软的 Silverlight5 也会在2021年停止维护,并完全终止后续新版本功能的开发。而 Java Applet 虽然还可以继续使用,但相较于早期上世纪90年代末,现在已然很少有人使用(不完全统计)。并且需要基于 JRE 来运行也使得 Applet 应用的运行成本大大提高。
代码混淆
在现代前端开发过程中,我们最常用的一种可以“降低源代码可读性”的方法就是使用“代码混淆”。通常意义上的代码混淆可以压缩原始 ASCII 代码的体积并将其中的诸如:变量、常量名用简短的毫无意义的标识符进行代替,这一步可以简单的理解为“去语义化”。以我们最常用的 “Uglify” 和 “GCC (Google Closure Compiler)” 为例,首先是一段未经代码混淆的原始 ECMAScript5 源代码:
let times = 0.1 * 8 + 1;function getExtra(n) { return [1, 4, 6].map(function(i) { return i * n; });}var arr = [8, 94, 15, 88, 55, 76, 21, 39];arr = getExtra(times).concat(arr.map(function(item) { return item * 2;}));function sortarr(arr) { for(i = 0; i < arr.length - 1; i++) { for(j = 0; j < arr.length - 1 - i; j++) { if(arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr;}console.log(sortarr(arr));
经过 UglifyJS3 的代码压缩混淆处理后的结果:
let times=1.8;function getExtra(r){return[1,4,6].map(function(t){return t*r})}var arr=[8,94,15,88,55,76,21,39];function sortarr(r){for(i=0;i-1;i++)for(j=0;j-1-i;j++)if(r[j]>r[j+1]){var t=r[j];r[j]=r[j+1],r[j+1]=t}return r}arr=getExtra(times).concat(arr.map(function(r){return 2*r})),console.log(sortarr(arr));
经过 Google Closure Compiler 的代码压缩混淆处理后的结果:
var b=[8,94,15,88,55,76,21,39];b=function(a){return[1,4,6].map(function(c){return c*a})}(1.8).concat(b.map(function(a){return 2*a}));console.log(function(a){for(i=0;i-1;i++)for(j=0;j-1-i;j++)if(a[j]>a[j+1]){var c=a[j];a[j]=a[j+1];a[j+1]=c}return a}(b));
对比上述两种工具的代码混淆压缩结果我们可以看到,UglifyJS 不会对原始代码进行“重写”,所有的压缩工作都是在代码原有结构的基础上进行的优化。而 GCC 对代码的优化则更靠近“编译器”,除了常见的变量、常量名去语义化外,还使用了常见的 DCE 优化策略,比如对常量表达式(constexpr)进行提前求值(0.1 * 8 + 1)、通过 “inline” 减少中间变量的使用等等。
UglifyJS 在处理优化 JavaScript 源代码时都是以其 AST 的形式进行分析的。比如在 Node.js 脚本中进行源码处理时,我们通常会首先使用 UglifyJS.parse 方法将一段 JavaScript 代码转换成其对应的 AST 形式,然后再通过 UglifyJS.Compressor 方法对这些 AST 进行处理。最后还需要通过 print_to_string 方法将处理后的 AST 结构转换成相应的 ASCII 可读代码形式。UglifyJS.Compressor 的本质是一个官方封装好的 “TreeTransformer” 类型,其内部已经封装好了众多常用的代码优化策略,而通过对 UglifyJS.TreeTransformer 进行适当的封装,我们也可以编写自己的代码优化器。如下所示我们编写了一个实现简单“常量传播”与“常量折叠”(注意这里其实是变量,但优化形式同 C++ 中的这两种基本优化策略相同)优化的 UglifyJS 转化器。
const UglifyJS = require('uglify-js');var symbolTable = {};var binaryOperations = { "+": (x, y) => x + y, "-": (x, y) => x - y, "*": (x, y) => x * y}var constexpr = new UglifyJS.TreeTransformer(null, function(node) { if (node instanceof UglifyJS.AST_Binary) { if (Number.isInteger(node.left.value) && Number.isInteger(node.right.value)) { return new UglifyJS.AST_Number({ value: binaryOperations[node.operator].call(this, Number(node.left.value), Number(node.right.value)) }); } else { return new UglifyJS.AST_Number({ value: binaryOperations[node.operator].call(this, Number(symbolTable[node.left.name].value), Number(symbolTable[node.right.name].value)) }) } } if (node instanceof UglifyJS.AST_VarDef) { // AST_VarDef -> AST_SymbolVar; // 通过符号表来存储已求值的变量值(UglifyJS.AST_Number)引用; symbolTable[node.name.name] = node.value; }});var ast = UglifyJS.parse(` var x = 10 * 2 + 6; var y = 4 - 1 * 100; console.log(x + y);`);// transform and print;ast.transform(constexpr);console.log(ast.print_to_string());// output: // var x=26;var y=-96;console.log(-70);
这里我们通过识别特定的 Uglify AST 节点类型(UglifyJS.AST_Binary / UglifyJS.AST_VarDef)来达到对代码进行精准处理的目的。可以看到,变量 x 和 y 的值在代码处理过程中被提前计算。不仅如此,其作为变量的值还被传递到了表达式 a + b 中,此时如果能够再结合简单的 DCE 策略便可以完成最初级的代码优化效果。类似的,其实通过 Babel 的 @babel/traverse 插件,我们也可以实现同样的效果,其所基于的原理也都大同小异,即对代码的 AST 进行相应的转换和处理。
WebAssembly
关于 Wasm 的基本介绍,这里我们不再多谈。那么到底应该如何利用 Wasm 的“字节码”特性来做到尽可能地做到“降低 JavaScript 代码可读性”这一目的呢?一个简单的 JavaScript 代码“加密”服务系统架构图如下所示:
这里整个系统分为两个处理阶段:
第一阶段:先将明文的 JavaScript 代码转换为基于特定 JavaScript 引擎(VM)的 OpCode 代码,这些二进制的 OpCode 代码会再通过诸如 Base64 等算法的处理而转换为经过编码的明文 ASCII 字符串格式;
第二阶段:将上述经过编码的 ASCII 字符串连同对应的 JavaScript 引擎内核代码统一编译成完整的 ASM / Wasm 模块。当模块在网页中加载时,内嵌的 JavaScript 引擎便会直接解释执行硬编码在模块中的、经过编码处理的 OpCode 代码;
比如我们以下面这段处于 Top-Level 层的 JavaScript 代码为例:
[1, 2, 3, 5, 6, 7, 8, 9].map(function(i) { return i * 2;}).reduce(function(p, i) { return p + i;}, 0);
按照正常的 VM 执行流程,上述代码在执行后会返回计算结果 82。这里我们以 JerryScript 这个开源的轻量级 JavaScript 引擎来作为例子,第一步首先将上述 ASCII 形式的代码 Feed 到该引擎中,然后便可以获得对应该引擎中间状态的 ByteCode 字节码。
然后再将这些二进制的字节码通过 Base64 算法编码成对应的可见字符形式。结果如下所示:
WVJSSgAAABYAAAAAAAAAgAAAAAEAAAAYAAEACAAJAAEEAgAAAAAABwAAAGcAAABAAAAAWDIAMhwyAjIBMgUyBDIHMgY6CCAIwAIoAB0AAToARscDAAAAAAABAAMBAQAhAgIBAQAAACBFAQCPAAAAAAABAAICAQAhAgICAkUBAIlhbQADAAYAcHVkZXIAAGVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
按照我们的架构思路,这部分被编码后的可见字符串会作为“加密”后的源代码被硬编码到包含有 VM 引擎核心的 Wasm 模块中。当模块被加载时,VM 会通过相反的顺序解码这段字符串,并得到二进制状态的 ByteCode。然后再通过一起打包进来的 VM 核心来执行这些中间状态的比特码。这里我们上述所提到的 ByteCode 实际上是以 JerryScript 内部的 SnapShot 快照结构存在于内存中的。最后这里给出上述 Demo 的主要部分源码,详细代码可以参考 Github:
#include "jerryscript.h"#include "cppcodec/base64_rfc4648.hpp"#include #include #define BUFFER_SIZE 256#ifdef WASM#include "emscripten.h"#endifstd::string encode_code(const jerry_char_t*, size_t);const unsigned char* transferToUC(const uint32_t* arr, size_t length) { auto container = std::vector<unsigned char>(); for (size_t x = 0; x < length; x++) { auto _t = arr[x]; container.push_back(_t >> 24); container.push_back(_t >> 16); container.push_back(_t >> 8); container.push_back(_t); } return &container[0];}std::vector<uint32_t> transferToU32(const uint8_t* arr, size_t length) { auto container = std::vector<uint32_t>(); for (size_t x = 0; x < length; x++) { size_t index = x * 4; uint32_t y = (arr[index + 0] << 24) | (arr[index + 1] << 16) | (arr[index + 2] << 8) | arr[index + 3]; container.push_back(y); } return container;}int main (int argc, char** argv) { const jerry_char_t script_to_snapshot[] = u8R"( [1, 2, 3, 5, 6, 7, 8, 9].map(function(i) { return i * 2; }).reduce(function(p, i) { return p + i; }, 0); )"; std::cout << encode_code(script_to_snapshot, sizeof(script_to_snapshot)) << std::endl; return 0;}std::string encode_code(const jerry_char_t script_to_snapshot[], size_t length) { using base64 = cppcodec::base64_rfc4648; // initialize engine; jerry_init(JERRY_INIT_SHOW_OPCODES); jerry_feature_t feature = JERRY_FEATURE_SNAPSHOT_SAVE; if (jerry_is_feature_enabled(feature)) { static uint32_t global_mode_snapshot_buffer[BUFFER_SIZE]; // generate snapshot; jerry_value_t generate_result = jerry_generate_snapshot( NULL, 0, script_to_snapshot, length - 1, 0, global_mode_snapshot_buffer, sizeof(global_mode_snapshot_buffer) / sizeof(uint32_t)); if (!(jerry_value_is_abort(generate_result) || jerry_value_is_error(generate_result))) { size_t snapshot_size = (size_t) jerry_get_number_value(generate_result); std::string encoded_snapshot = base64::encode( transferToUC(global_mode_snapshot_buffer, BUFFER_SIZE), BUFFER_SIZE * 4); jerry_release_value(generate_result); jerry_cleanup(); // encoded bytecode of the snapshot; return encoded_snapshot; } } return "[EOF]";}void run_encoded_snapshot(std::string code, size_t snapshot_size) { using base64 = cppcodec::base64_rfc4648; auto result = transferToU32( &(base64::decode(code)[0]), BUFFER_SIZE); uint32_t snapshot_decoded_buffer[BUFFER_SIZE]; for (auto x = 0; x < BUFFER_SIZE; x++) { snapshot_decoded_buffer[x] = result.at(x); } jerry_init(JERRY_INIT_EMPTY); jerry_value_t res = jerry_exec_snapshot( snapshot_decoded_buffer, snapshot_size, 0, 0); // default as number result; std::cout << "[Zero] code running result: " << jerry_get_number_value(res) << std::endl; jerry_release_value(res);}#ifdef WASMextern "C" { void EMSCRIPTEN_KEEPALIVE run_core() { // encoded snapshot (will be hardcoded in wasm binary file); std::string base64_snapshot = "WVJSSgAAABYAAAAAAAAAgAAAAAEAAAAYAAEACAAJAAEEAgAAAAAABwAAAGcAAABAAAAAWDIAMhwyAjIBMgUyBDIHMgY6CCAIwAIoAB0AAToARscDAAAAAAABAAMBAQAhAgIBAQAAACBFAQCPAAAAAAABAAICAQAhAgICAkUBAIlhbQADAAYAcHVkZXIAAGVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; run_encoded_snapshot(base64_snapshot, 142); }}#endif
当然这里我们只是基于 JerryScript 做了一个利用 Wasm 进行 JavaScript 代码“加密”的最简单 Demo,代码并没有处理边界 Case,对于非 Top-Level 的代码也并没有进行测试。如果需要进一步优化,我们可以思考如何利用 “jerry-libm” 来处理 JavaScript 中诸如 Math.abs 等常见标准库;对于平台依赖的符号(比如 window.document 等平台依赖的函数或变量)怎样通过 Wasm 的导出段与导入段进行处理等等。最后,给朋友的产品打个广告,SecurityWorker 一个基于 Wasm 进行前端代码“加密”的工具软件,其大体思路与我们上述介绍的基本一致,但功能和 API 更加完善,并且其利用了 Worker Thread 将前端代码的核心计算逻辑与主线程进行分离,性能更高、加壳后的代码体积更小,这里给个赞!
文章转载于(https://www.yhspy.com/2019/04/10/浅谈前端代码加密/)
-
关于mybatis如何获取入参是List和Map的取值问题
2020-10-17 14:23:55,需要循环List,然后通过List循环出来的值为Key获取Map中的值作为sql的入参,遇到了一些问题。但是经过不懈的努力,最后终于解决了这个问题。顺便分享一下自己的经验。 项目结构 pom文件配置 server: port: ...前言
最近在工作中需要使用到mybatis,需要实现某个功能。但是发现需要编写一个sql,但是mybatis的映射文件入参是List集合和Map<String,Integer>,需要循环List,然后通过List循环出来的值为Key获取Map中的值作为sql的入参,遇到了一些问题。但是经过不懈的努力,最后终于解决了这个问题。顺便分享一下自己的经验。
-
项目结构
-
pom文件配置
server: port: 8379 spring: datasource: username: root url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC driver-class-name: com.mysql.jdbc.Driver mybatis: mapper-locations: classpath:mapping/*Mapper.xml type-aliases-package: com.example.bean #showSql logging: level: com: example: dao : debug
3 其他的业务代码
package com.example.bean; import lombok.Data; @Data public class SysUser { private Integer id; private String username; private String image; }
package com.example.controller; import com.example.bean.SysUser; import com.example.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController public class SysUserController { @Autowired private SysUserService sysUserService; @GetMapping("/getSysUserById/{id}") SysUser getSysUserById(@PathVariable("id") String id) { List<String> ids = new ArrayList<>(); Map<String, Integer> infoMap = new HashMap<>(); infoMap.put("1", 1); infoMap.put("8", 8); infoMap.put("9", 9); ids.add(id); return sysUserService.getSysUserById(ids, infoMap); } }
package com.example.dao; import com.example.bean.SysUser; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; @Mapper public interface SysUserMapper { SysUser getSysUserById(@Param("ids") List<String> ids,@Param("infoMap") Map<String,Integer> infoMap); }
package com.example.service; import com.example.bean.SysUser; import com.example.dao.SysUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; @Service public class SysUserService { @Autowired private SysUserMapper sysUserMapper; public SysUser getSysUserById(List<String> ids, Map<String, Integer> infoMap) { return sysUserMapper.getSysUserById(ids,infoMap); } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.dao.SysUserMapper"> <select id="getSysUserById" resultType="com.example.bean.SysUser"> <foreach collection="ids" item="id"> <bind name="userId" value="infoMap[id]"/> select * from sys_user where id = #{userId} </foreach> </select> </mapper>
第一种采用#符的取值法,是可以防止sql注入的,可以通过循环List获取Map的值<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.dao.SysUserMapper"> <select id="getSysUserById" resultType="com.example.bean.SysUser"> <foreach collection="ids" item="id"> <!-- <bind name="userId" value="infoMap[id]"/>--> select * from sys_user where id = '${infoMap[id]}' </foreach> </select> </mapper>
第二种方式采用$符的取值法,也可以获取,虽然不能防止sql注入,但是可以做白名单的校验,防止sql注入3 总结
本人小白一枚,在工作中经常遇到问题,希望通过解决问题,将自己的经验分享给大家。有问题希望大家多多指出来。能够和大家互相交流 -
-
java反射获取对象的字段名及值(工具类)
2018-10-14 21:59:37问题:如何将实体entity转化为map类型,并传给数组的解法或方法,获取其中的某个字段。 解决方法:entity向map转化,全部传入的方法这样做效率较低。 下面方法将我们需要的字段反射出去,并将字段的值传回方法中,... -
jstl中list嵌套map
2009-07-10 13:36:47如何展示model本身是list,list下的每个值都是map。 请参考如下: (1.1) JSP 代码 11111 ... (请注意:里面使用了两次forEach,一次用来获取list中某个元素,一次用于获取Map中的entry)... -
jstl中list嵌套map, 取值
2014-12-11 15:05:05如何展示model本身是list,list下的每个值都是map。 请参考如下: (1.1) JSP 代码 ... (请注意:里面使用了两次forEach,一次用来获取list中某个元素,一次用于获取Map中的entry) (1.2 -
Spring 架构下使用jstl在页面上展示map或者list(包括类型嵌套的数据)
2005-12-03 23:20:00如何展示model本身是list,list下的每... 请参考如下: (1.1) JSP 代码 11111 name: age: (请注意:里面使用了两次forEach,一次用来获取list中某个元素,一次用于获取Map中的entry) (1.2) 对应Java程序代码 -
Spring——如何为不同数据类型参数赋值
2020-11-06 17:32:44如何为构造方法中不同数据类型参数赋值bean 标签constructor-arg子标签通过构造方法为不同数据类型参数赋值无参构造...为bean标签添加id和name属性都可以通过调用getBean(id属性值或name某个属性值)方法获取到Spring -
C++ 数据结构实战:快速查找
2019-03-27 17:36:03给定m个商品ID, 4个特征(特征数量可增可减),已知商品对应的每个特征的值,如何快速获取某个商品的某个特征值? 当时我采取的是stl嵌套的数据结构,由于时间复杂度较高,且vector的值是随着map的rehash阶段不断... -
超级有影响力霸气的Java面试题大全文档
2012-07-18 09:47:04引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始... -
内存(二)
2018-03-08 17:09:45前言: 项目开发过程中经常需要...下面就介绍.map文件以及如何根据.map文件获取占用空间的统计。 概念: (一)C语言五大内存分区 1、栈区(stack) 由编译器自动分配释放 ,存放函数的参数值,局部变量的... -
深入到源码:解读 redux 的设计思路与用法
2020-12-30 12:58:03- action 返回的 plain object 中包含属性为 type 的常量值 - 表明这个对象里携带的其他数据应该被如何「再处理」 - 或者不带其他数据,仅仅启示已有数据需要如何调整,或者需要主动获取哪些数据 <p>... -
java 面试题 总结
2009-09-16 08:45:34引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始... -
Java并发编程(学习笔记).xmind
2020-06-19 15:54:23(1)用作异步任务使用,且可以使用get方法获取任务的结果 (2)用于表示一些时间较长的计算 状态 等待运行 正在运行 运行完成 使用Callable对象实例化FutureTask类 ... -
2.5.6 在NetWare 网络中,客户需要访问某个类型的服务器时,首先要发送一个 ()广播报文来寻找服务器? 2.5.7 IPX地址网络地址有( )个字节? 2.5.8 对于帧中继描述正确的是? 2.5.9 对于INVERSE ARP的描述正确...
-
Cassandra 权威指南(Apache Cassandra 项目主席作序推荐)--详细书签版
2013-02-04 13:39:05书中介绍了它无中心架构、高可用、无缝扩展等引人注目的特点,讲述了如何安装、配置cassandra及如何在其上运行实例,还介绍了对它的监控、维护和性能调优手段,同时还涉及了cassandra相关的集成工具hadoop及其类似的... -
SSO-WebDemo
2013-08-12 20:25:57web服务器设置了一个cookie,并将这个cookie和页面1一起返回给浏览器,浏览器接到cookie之后,就会保存起来,在它访问页面2的时候会把这个cookie也带上,Web服务器接到请求时也能读出cookie的值,根据cookie值的内容... -
JAVA面试题最全集
2010-03-13 13:09:10如何获取某个日期是当月的最后一天 如何格式化日期 5.数组和集合 6.文件和目录(I/O)操作 如何列出某个目录下的所有文件 如何列出某个目录下的所有子目录 判断一个文件或目录是否存在 如何读写文件 7.Java... -
操作系统(内存管理)
2009-09-20 12:55:25因此,如果一个进程运行超出了它初始分配的内存,那么它必须请求操作系统“映射进来(map in)”更多的内存。(映射是一个表示一一对应关系的数学术语 —— 当内存的虚拟地址有一个对应的物理地址来存储内存内容时,... -
内存管理内存管理内存管理
2011-04-04 20:16:26因此,如果一个进程运行超出了它初始分配的内存,那么它必须请求操作系统“映射进来(map in)”更多的内存。(映射是一个表示一一对应关系的数学术语 —— 当内存的虚拟地址有一个对应的物理地址来存储内存内容时... -
3.1 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(保证可见性) 3.2 禁止进行指令重排序。(保证有序性) 划个重点:volatile 关键字能保证...
-
JavaScript Table行定位效果
2009-05-20 22:59:13table的border属性用来指定边框宽度,table特有的frame属性是用来设置或获取表格周围的边框显示的方式。 w3c的tabel的frame部分说明frame可以是以下值: void: No sides. This is the default value. above: The ... -
网管教程 从入门到精通软件篇.txt
2010-04-25 22:43:49map 命令还显示文件系统的类型和每个磁盘的大小(MB)。 二。★★★常见文件扩展名和它们的说明 常见文件扩展名和它们的说明 A ACE:Ace压缩档案格式 ACT:Microsoft office助手文件 AIF,AIFF:音频互交换... -
华为编程开发规范与案例
2008-09-04 16:44:56经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为0xFFFFFFFF,该值是从被破坏的内存数据库中获取的,正常情况下该值小于127。而pDBFat是数据库的起始地址,如果pSysHead->dbf_count值异常过大,将... -
Java SE 6.0编程指南 源码
2012-05-18 13:40:45同时我把目录页提供上来,本书的实例命名是以章节号命名的,比如:sample13_7,表示这是第13章 第7小节的实例,只能对应到二级目录,而书却又三级目录,所以想要找某个实例还是比较麻烦的。但通过目录对照还是很方便... -
arcgis工具
2012-10-22 22:37:31利用这种方法,选择被另一图层要素覆盖的某个图层上的要素。例如,搜索道路跨越的荒地,得到的结果是道路跨越其边界的所有荒地。 相交(Intersect) 与Are crossed by the outline of类似,但是该方法可以选择与... -
导师计划--数据结构和算法系列(上)
2020-12-09 04:46:22<p><strong>indexOf方法返回某个指定字符串值在数组中的位置。<code>searchValue是查询的字符串;<code>fromIndex是查询的开始位置,默认是0。如果查询不到,会返回-1。 </li><li> <p>concat... -
34_ElasticSearch bucket+metric:统计每种分组中的平均值 35_ElasticSearch bucket嵌套实现颜色+品牌的多层下钻分析 36_ElasticSearch 统计每种颜色电视最大最小价格 37_ElasticSearch hitogram按价格区间...
-
DHLO-2021Spring:DHLO2021:数字健康实验室公开讲座系列-2021Spring-源码
-
java雍俊海_JAVA程序设计 雍俊海(学习笔记3)
-
2021年 系统分析师 系列课
-
(初学者)Django报错:import error:cannot import name ‘index3’
-
wftest:测试上传-源码
-
VisionShow.rar
-
Python函数库深度详解(1)
-
YTPlayerView 屏蔽画中画功能
-
借助多准则决策系统进行主动学习
-
MySQL 高可用(DRBD + heartbeat)
-
期刊论文发表时可以借鉴他人的文献吗?
-
实现 MySQL 读写分离的利器 mysql-proxy
-
华为1+X——网络系统建设与运维(高级)
-
Unity RUST 逆向安全开发
-
规则的激荡与新生:2020数据治理年度报告-腾讯研究院-2021-96页.pdf
-
koronapp-frontend-mobile-源码
-
MySQL 性能优化(思路拓展及实操)
-
安装Code Blocks时出现can‘t find compiler的解决方法
-
access应用的3个开发实例
-
韩泰轮胎:春节回家,你的汽车摸底检查了吗?