title: 从零玩转人脸识别验证
date: 2022-05-15 21:05:52.974
updated: 2023-05-16 00:00:11.594
url: http://www.yby6.com/archives/face
categories:
- Java分类
- 从零玩转系列
- 人脸识别
tags:
- java
- 人脸识别
(前往享受人脸识别)
本期教程人脸识别第三方平台为虹软科技,本文章讲解的是人脸识别RGB活体追踪技术,免费的功能很多可以自行搭配,希望在你看完本章课程有所收获。
ArcFace 离线SDK,包含人脸检测、性别检测、年龄检测、人脸识别、图像质量检测、RGB活体检测、IR活体检测等能力,初次使用时需联网激活,激活后即可在本地无网络环境下工作,可根据具体的业务需求结合人脸识别SDK灵活地进行应用层开发。
对传入的图像数据进行人脸检测,返回人脸的边框以及朝向信息,可用于后续的人脸识别、特征提取、活体检测等操作;
对来自于视频流中的图像数据,进行人脸检测,并对检测到的人脸进行持续跟踪。(我们是实时的所以就只能使用第三方操作,先不使用这个)
提取人脸特征信息,用于人脸的特征比对。
人脸属性,支持检测年龄、性别以及3D角度。
人脸3D角度:俯仰角(pitch), 横滚角(roll), 偏航角(yaw)。
离线活体检测,静默式识别,在人脸识别过程中判断操作用户是否为真人,有效防御照片、视频、纸张等不同类型的作弊攻击,提高业务安全性,让人脸识别更安全、更快捷,体验更佳。支持单目RGB活体检测、双目(IR/RGB)活体检测,可满足各类人脸识别终端产品活体检测应用。
http://ai.arcsoft.com.cn/technology/faceTracking.html
进入开发者中心进行注册以及认证个人信息
导入项目依赖
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<druid-spring-boot-starter.version>1.2.6</druid-spring-boot-starter.version>
<mybatis-spring-boot.version>2.1.4</mybatis-spring-boot.version>
<pagehelper.boot.version>1.3.0</pagehelper.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 人脸识别 -->
<dependency>
<groupId>com.arcsoft.face</groupId>
<artifactId>arcsoft-sdk-face</artifactId>
<scope>system</scope>
<systemPath>${project.basedir}/lib/arcsoft-sdk-face-3.0.0.0.jar</systemPath>
<version>3.0.0.0</version>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus 增强CRUD -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- pagehelper 分页插件 内置mybatis 依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
</dependency>
<!-- 阿里数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- JSON工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0</version>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>top.yangbuyi.YangbuyiFaceDemoApplication</mainClass>
<!-- 文件要配置includeSystemScope属性,否则可能会导致arcsoft-sdk-face-3.0.0.0.jar获取不到 -->
<includeSystemScope>true</includeSystemScope>
<fork>true</fork>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
使用代码生成器生成CRUD
版本请对应图片当中的jebat全家桶群里获取3秒破解使用
生成完毕后在根目录创建lib目录将下载下来的人脸识别依赖导入->右击添加到库
application.yml 修改配置文件
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# tomcat最大线程数,默认为200
max-threads: 800
# Tomcat启动初始化的线程数,默认值25
min-spare-threads: 30
# Spring配置
spring:
# 同时执行其它配置文件
profiles:
active: druid
mvc: # 把前端的接收到的时间格式 格式化为 yyyy-MM-dd HH:mm:ss
date-format: yyyy-MM-dd HH:mm:ss
jackson: # 把后台的时间格式 格式化为 yyyy-MM-dd HH:mm:ss
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# MyBatis配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: top.yangbuyi.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
# 人脸识别配置
# WIND64
config:
sdk-lib-path: M:\yangbuyiya-RBAC\libs\WIN64
app-id: 4QKtmacvsKqaCsoXyyujcs21JTAr79pTczPdZpuaEjhH
sdk-key: EgBjrmidnqstaL46msfHukeKanYXCujzeHokf2qcC3br
thread-pool-size: 5
application-druid.yml 数据源配置
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
url: jdbc:mysql://127.0.0.1:3308/face?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /yangbuyi/druid/*
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
resources 下创建mybatis文件夹创建mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/> <!-- 全局映射器启用缓存 -->
<setting name="useGeneratedKeys" value="true"/> <!-- 允许 JDBC 支持自动生成主键 -->
<setting name="defaultExecutorType" value="REUSE"/> <!-- 配置默认的执行器 -->
<setting name="logImpl" value="SLF4J"/> <!-- 指定 MyBatis 所用日志的具体实现 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> 驼峰式命名 -->
</settings>
</configuration>
创建config文件
创建ApplicationConfig全局配置类
/**
* 程序注解配置
*
* @author yangbuyi
*/
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("top.yangbuyi.mapper")
public class ApplicationConfig {
/**
* 时区配置
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization () {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
/**
* 处理Long类型精度丢失
*
* @return
*/
@Bean("jackson2ObjectMapperBuilderCustomizer")
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer () {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
.serializerByType(Long.TYPE, ToStringSerializer.instance);
}
}
创建全局MybatisPlusConfig配置
/**
* @program: yangbuyi-rbac
* @ClassName: MybatisPlusConfig
* @create: 2022-04-25 15:48
* @author: yangbuyi.top
* @since: JDK1.8
* @MybatisPlusConfig: $
**/
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor () {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型 http://baomidou.com/guide/interceptor-pagination.html
*/
public PaginationInnerInterceptor paginationInnerInterceptor () {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件 http://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor () {
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作 http://baomidou.com/guide/interceptor-block-attack.html
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor () {
return new BlockAttackInnerInterceptor();
}
}
创建人脸返回实体 FaceSearchResDto
/**
* @author yangbuyi.top
*/
@Data
public class FaceSearchResDto {
/**
* 唯一人脸Id
*/
private String faceId;
/**
* 人脸名称
*/
private String name;
private Integer similarValue;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private String gender;
/**
* 图片
*/
private String image;
}
创建人脸映射 FaceUserInfo
/**
* @author yangbuyi.top
*/
@Data
public class FaceUserInfo {
private int id;
private int groupId;
private String faceId;
private String name;
private Integer similarValue;
private byte[] faceFeature;
}
创建年龄映射
/**
* @author yangbuyi.top
*/
public class ProcessInfo {
private Integer age;
private Integer gender;
public Integer getAge () {
return age;
}
public void setAge (Integer age) {
this.age = age;
}
public Integer getGender () {
return gender;
}
public void setGender (Integer gender) {
this.gender = gender;
}
}
创建ErrorCodeEnum枚举类
/**
* @author yangbuyi.top
*/
public enum ErrorCodeEnum {
MOK(0, "成功"),
UNKNOWN(1, "未知错误"),
INVALID_PARAM(2, "无效参数"),
UNSUPPORTED(3, "引擎不支持"),
NO_MEMORY(4, "内存不足"),
BAD_STATE(5, "状态错误"),
USER_CANCEL(6, "用户取消相关操作"),
EXPIRED(7, "操作时间过期"),
USER_PAUSE(8, "用户暂停操作"),
BUFFER_OVERFLOW(9, "缓冲上溢"),
BUFFER_UNDERFLOW(10, "缓冲下溢"),
NO_DISKSPACE(11, "存贮空间不足"),
COMPONENT_NOT_EXIST(12, "组件不存在"),
GLOBAL_DATA_NOT_EXIST(13, "全局数据不存在"),
NO_FACE_DETECTED(14, "未检出到人脸"),
FACE_DOES_NOT_MATCH(15, "人脸不匹配"),
INVALID_APP_ID(28673, "无效的AppId"),
INVALID_SDK_ID(28674, "无效的SdkKey"),
INVALID_ID_PAIR(28675, "AppId和SdkKey不匹配"),
MISMATCH_ID_AND_SDK(28676, "SdkKey 和使用的SDK 不匹配"),
SYSTEM_VERSION_UNSUPPORTED(28677, "系统版本不被当前SDK所支持"),
LICENCE_EXPIRED(28678, "SDK有效期过期,需要重新下载更新"),
APS_ENGINE_HANDLE(69633, "引擎句柄非法"),
APS_MEMMGR_HANDLE(69634, "内存句柄非法"),
APS_DEVICEID_INVALID(69635, " Device ID 非法"),
APS_DEVICEID_UNSUPPORTED(69636, "Device ID 不支持"),
APS_MODEL_HANDLE(69637, "模板数据指针非法"),
APS_MODEL_SIZE(69638, "模板数据长度非法"),
APS_IMAGE_HANDLE(69639, "图像结构体指针非法"),
APS_IMAGE_FORMAT_UNSUPPORTED(69640, "图像格式不支持"),
APS_IMAGE_PARAM(69641, "图像参数非法"),
APS_IMAGE_SIZE(69642, "图像尺寸大小超过支持范围"),
APS_DEVICE_AVX2_UNSUPPORTED(69643, "处理器不支持AVX2指令"),
FR_INVALID_MEMORY_INFO(73729, "无效的输入内存"),
FR_INVALID_IMAGE_INFO(73730, "无效的输入图像参数"),
FR_INVALID_FACE_INFO(73731, "无效的脸部信息"),
FR_NO_GPU_AVAILABLE(73732, "当前设备无GPU可用"),
FR_MISMATCHED_FEATURE_LEVEL(73733, "待比较的两个人脸特征的版本不一致"),
FACEFEATURE_UNKNOWN(81921, "人脸特征检测错误未知"),
FACEFEATURE_MEMORY(81922, "人脸特征检测内存错误"),
FACEFEATURE_INVALID_FORMAT(81923, "人脸特征检测格式错误"),
FACEFEATURE_INVALID_PARAM(81924, "人脸特征检测参数错误"),
FACEFEATURE_LOW_CONFIDENCE_LEVEL(81925, "人脸特征检测结果置信度低"),
ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_INIT(86017, "Engine不支持的检测属性"),
ASF_EX_BASE_FEATURE_UNINITED(86018, "需要检测的属性未初始化"),
ASF_EX_BASE_FEATURE_UNPROCESSED(86019, "待获取的属性未在process中处理过"),
ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_PROCESS(86020, "PROCESS不支持的检测属性,例如FR,有自己独立的处理函数"),
ASF_EX_BASE_INVALID_IMAGE_INFO(86021, "无效的输入图像"),
ASF_EX_BASE_INVALID_FACE_INFO(86022, "无效的脸部信息"),
ASF_BASE_ACTIVATION_FAIL(90113, "人脸比对SDK激活失败,请打开读写权限"),
ASF_BASE_ALREADY_ACTIVATED(90114, "人脸比对SDK已激活"),
ASF_BASE_NOT_ACTIVATED(90115, "人脸比对SDK未激活"),
ASF_BASE_SCALE_NOT_SUPPORT(90116, "detectFaceScaleVal 不支持"),
ASF_BASE_VERION_MISMATCH(90117, "SDK版本不匹配"),
ASF_BASE_DEVICE_MISMATCH(90118, "设备不匹配"),
ASF_BASE_UNIQUE_IDENTIFIER_MISMATCH(90119, "唯一标识不匹配"),
ASF_BASE_PARAM_NULL(90120, "参数为空"),
ASF_BASE_SDK_EXPIRED(90121, "SDK已过期"),
ASF_BASE_VERSION_NOT_SUPPORT(90122, "版本不支持"),
ASF_BASE_SIGN_ERROR(90123, "签名错误"),
ASF_BASE_DATABASE_ERROR(90124, "数据库插入错误"),
ASF_BASE_UNIQUE_CHECKOUT_FAIL(90125, "唯一标识符校验失败"),
ASF_BASE_COLOR_SPACE_NOT_SUPPORT(90126, "输入的颜色空间不支持"),
ASF_BASE_IMAGE_WIDTH_NOT_SUPPORT(90127, "输入图像的byte数据长度不正确"),
ASF_NETWORK_BASE_COULDNT_RESOLVE_HOST(94209, "无法解析主机地址"),
ASF_NETWORK_BASE_COULDNT_CONNECT_SERVER(94210, "无法连接服务器"),
ASF_NETWORK_BASE_CONNECT_TIMEOUT(94211, "网络连接超时"),
ASF_NETWORK_BASE_UNKNOWN_ERROR(94212, "未知错误");
private Integer code;
private String description;
ErrorCodeEnum (Integer code, String description) {
this.code = code;
this.description = description;
}
public Integer getCode () {
return code;
}
public void setCode (Integer code) {
this.code = code;
}
public String getDescription () {
return description;
}
public void setDescription (String description) {
this.description = description;
}
public static ErrorCodeEnum getDescriptionByCode (Integer code) {
for (ErrorCodeEnum errorCodeEnum : ErrorCodeEnum.values()) {
if (code.equals(errorCodeEnum.getCode())) {
return errorCodeEnum;
}
}
return ErrorCodeEnum.UNKNOWN;
}
}
创建Base64DecodeMultipartFile
package top.yangbuyi.utils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
/**
* @author yangbuyi.top
* @program: yangbuyi-rbac
* @ClassName: Base64DecodeMultipartFile
* @create: 2022-04-25 16:25
* @since: JDK1.8
* @Base64DecodeMultipartFile: $
**/
public class Base64DecodeMultipartFile implements MultipartFile {
private final byte[] imgContent;
private final String header;
public Base64DecodeMultipartFile (byte[] imgContent, String header) {
this.imgContent = imgContent;
this.header = header.split(";")[0];
}
@Override
public String getName () {
return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
}
@Override
public String getOriginalFilename () {
return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
}
@Override
public String getContentType () {
return header.split(":")[1];
}
@Override
public boolean isEmpty () {
return imgContent == null || imgContent.length == 0;
}
@Override
public long getSize () {
return imgContent.length;
}
@Override
public byte[] getBytes () throws IOException {
return imgContent;
}
@Override
public InputStream getInputStream () throws IOException {
return new ByteArrayInputStream(imgContent);
}
@Override
public void transferTo (File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(imgContent);
}
}
创建ImageUtils
package top.yangbuyi.utils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
/**
* 图片处理工具类
*
* @author yangbuyi.top
*/
public class ImageUtils {
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
public static MultipartFile base64ToMultipartFile (String base64) {
//base64编码后的图片有头信息所以要分离出来 [0]data:image/png;base64, 图片内容为索引[1]
String[] baseStrs = base64.split(",");
//取索引为1的元素进行处理
byte[] b = Base64.decodeBase64(baseStrs[1]);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
//处理过后的数据通过Base64DecodeMultipartFile转换为MultipartFile对象
return new Base64DecodeMultipartFile(b, baseStrs[0]);
}
}
项目基本需要的配置与工具已经创建完毕接下来我们开始人脸识别业务编写
创建factory文件夹->创建FaceEngineFactory类
import com.arcsoft.face.EngineConfiguration;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
* 引擎工厂
* @author yangbuyi.top
*/
@Slf4j
public class FaceEngineFactory extends BasePooledObjectFactory<FaceEngine> {
private final String appId;
private final String sdkKey;
private final String sdkLibPath;
private final EngineConfiguration engineConfiguration;
private final Integer detectFaceMaxNum = 10;
private final Integer detectFaceScaleVal = 16;
private final DetectMode detectMode = DetectMode.ASF_DETECT_MODE_IMAGE;
private final DetectMode detectVideo = DetectMode.ASF_DETECT_MODE_VIDEO;
private final DetectOrient detectFaceOrientPriority = DetectOrient.ASF_OP_0_ONLY;
public FaceEngineFactory (String sdkLibPath, String appId, String sdkKey, EngineConfiguration engineConfiguration) {
this.sdkLibPath = sdkLibPath;
this.appId = appId;
this.sdkKey = sdkKey;
this.engineConfiguration = engineConfiguration;
}
@Override
public FaceEngine create () throws Exception {
FaceEngine faceEngine = new FaceEngine(sdkLibPath);
// 用于在线激活SDK---
int activeCode = faceEngine.activeOnline(appId, sdkKey);
log.info("在线激活SDK完毕!,{}", activeCode);
int initCode = faceEngine.init(engineConfiguration);
log.info("初始化功能引擎完毕!,{}", initCode);
return faceEngine;
}
@Override
public PooledObject<FaceEngine> wrap (FaceEngine faceEngine) {
return new DefaultPooledObject<>(faceEngine);
}
@Override
public void destroyObject (PooledObject<FaceEngine> p) throws Exception {
FaceEngine faceEngine = p.getObject();
int unInitCode = faceEngine.unInit();
super.destroyObject(p);
log.info("销毁对象完毕! faceEngineUnInitCode: {}", unInitCode);
}
}
在service目录下创建-> FaceEngineService
package top.yangbuyi.service;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.toolkit.ImageInfo;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* 人脸识别接口
*/
public interface FaceEngineService {
/**
* 人脸检测
* @param imageInfo
* @return
*/
List<FaceInfo> detectFaces (ImageInfo imageInfo);
/**
* 提取年龄-性别
* @param imageInfo
* @return
*/
List<ProcessInfo> process (ImageInfo imageInfo);
/**
* 人脸特征
* @param imageInfo
* @return
*/
byte[] extractFaceFeature (ImageInfo imageInfo) throws InterruptedException;
/**
* 人脸比对
* @param groupId
* @param faceFeature
* @return
*/
List<FaceUserInfo> compareFaceFeature (byte[] faceFeature, Integer groupId) throws InterruptedException, ExecutionException;
}
service.impl 包下创建 FaceEngineServiceImpl 实现类
import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.toolkit.ImageInfo;
import com.google.common.collect.Lists;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;
import top.yangbuyi.factory.FaceEngineFactory;
import top.yangbuyi.mapper.SysUserFaceInfoMapper;
import top.yangbuyi.service.FaceEngineService;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @author yangbuyi.top
*/
@Service
public class FaceEngineServiceImpl implements FaceEngineService {
public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class);
@Value("${config.sdk-lib-path}")
public String sdkLibPath;
@Value("${config.app-id}")
public String appId;
@Value("${config.sdk-key}")
public String sdkKey;
@Value("${config.thread-pool-size}")
public Integer threadPoolSize;
/**
* 人脸识别引擎
*/
private static FaceEngine faceEngine;
/**
* 相似度
*/
private final Integer passRate = 95;
private ExecutorService executorService;
@Autowired
private SysUserFaceInfoMapper userFaceInfoMapper;
/**
* 线程池
*/
private GenericObjectPool<FaceEngine> faceEngineObjectPool;
/**
* 项目启动时初始化线程池与人脸识别引擎配置
*/
@PostConstruct
public void init () {
executorService = Executors.newFixedThreadPool(threadPoolSize);
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(threadPoolSize);
poolConfig.setMaxTotal(threadPoolSize);
poolConfig.setMinIdle(threadPoolSize);
poolConfig.setLifo(false);
//引擎配置
EngineConfiguration engineConfiguration = new EngineConfiguration();
engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
//功能配置 对应的功能请查看文档
FunctionConfiguration functionConfiguration = new FunctionConfiguration();
functionConfiguration.setSupportAge(true);
functionConfiguration.setSupportFace3dAngle(true);
functionConfiguration.setSupportFaceDetect(true);
functionConfiguration.setSupportFaceRecognition(true);
functionConfiguration.setSupportGender(true);
functionConfiguration.setSupportLiveness(true);
functionConfiguration.setSupportIRLiveness(true);
engineConfiguration.setFunctionConfiguration(functionConfiguration);
// 底层库算法对象池
faceEngineObjectPool = new GenericObjectPool(new FaceEngineFactory(sdkLibPath, appId, sdkKey, engineConfiguration), poolConfig);
try {
faceEngine = faceEngineObjectPool.borrowObject();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 确保精度
* @param value
* @return
*/
private int plusHundred (Float value) {
BigDecimal target = new BigDecimal(value);
BigDecimal hundred = new BigDecimal(100f);
return target.multiply(hundred).intValue();
}
}
1、必须进行人脸特征获取 -> 特征获取成功 -> 进入人脸对比 -> 人脸检测 -> 返回人脸数据
2、必须进行人脸特征获取 -> 特征获取失败 -> 直接跳出返回 未检出到人脸
/**
* 人脸特征
*
* @param imageInfo
* @return
*/
@Override
public byte[]extractFaceFeature(ImageInfo imageInfo)throws InterruptedException{
FaceEngine faceEngine=null;
try{
//获取引擎对象
faceEngine=faceEngineObjectPool.borrowObject();
//人脸检测得到人脸列表
List<FaceInfo> faceInfoList=new ArrayList<FaceInfo>();
//人脸检测
int i=faceEngine.detectFaces(imageInfo.getImageData(),imageInfo.getWidth(),imageInfo.getHeight(),imageInfo.getImageFormat(),faceInfoList);
if(CollectionUtil.isNotEmpty(faceInfoList)){
FaceFeature faceFeature=new FaceFeature();
//提取人脸特征
faceEngine.extractFaceFeature(imageInfo.getImageData(),imageInfo.getWidth(),imageInfo.getHeight(),imageInfo.getImageFormat(),faceInfoList.get(0),faceFeature);
return faceFeature.getFeatureData();
}
}catch(Exception e){
logger.error("",e);
}finally{
if(faceEngine!=null){
//释放引擎对象
faceEngineObjectPool.returnObject(faceEngine);
}
}
return null;
}
/**
* 人脸比对
* @param groupId
* @param faceFeature
* @return 人脸组
*/
@Override
public List<FaceUserInfo> compareFaceFeature(byte[]faceFeature,Integer groupId)throws InterruptedException,ExecutionException{
// 识别到的人脸列表
List<FaceUserInfo> resultFaceInfoList=Lists.newLinkedList();
// 创建人脸特征对象
FaceFeature targetFaceFeature=new FaceFeature();
targetFaceFeature.setFeatureData(faceFeature);
// 根据分组拿人脸库,从数据库中取出人脸库
List<FaceUserInfo> faceInfoList=userFaceInfoMapper.getUserFaceInfoByGroupId(groupId);
// 分成50一组,多线程处理, 数据量大1000组
List<List<FaceUserInfo>>faceUserInfoPartList=Lists.partition(faceInfoList,50);
// 多线程
CompletionService<List<FaceUserInfo>>completionService=new ExecutorCompletionService(executorService);
for(List<FaceUserInfo> part:faceUserInfoPartList){
// 开始线程扫描人脸匹配度
completionService.submit(new CompareFaceTask(part,targetFaceFeature));
}
// 获取线程任务参数
for(List<FaceUserInfo> faceUserInfos:faceUserInfoPartList){
List<FaceUserInfo> faceUserInfoList=completionService.take().get();
if(CollectionUtil.isNotEmpty(faceInfoList)){
resultFaceInfoList.addAll(faceUserInfoList);
}
}
// 从大到小排序
resultFaceInfoList.sort((h1,h2)->h2.getSimilarValue().compareTo(h1.getSimilarValue()));
return resultFaceInfoList;
}
/**
* 多线程跑人脸对比逻辑
*/
private class CompareFaceTask implements Callable<List<FaceUserInfo>> {
private final List<FaceUserInfo> faceUserInfoList;
private final FaceFeature targetFaceFeature;
public CompareFaceTask (List<FaceUserInfo> faceUserInfoList, FaceFeature targetFaceFeature) {
this.faceUserInfoList = faceUserInfoList;
this.targetFaceFeature = targetFaceFeature;
}
@Override
public List<FaceUserInfo> call () throws Exception {
FaceEngine faceEngine = null;
//识别到的人脸列表
List<FaceUserInfo> resultFaceInfoList = Lists.newLinkedList();
try {
faceEngine = faceEngineObjectPool.borrowObject();
for (FaceUserInfo faceUserInfo : faceUserInfoList) {
FaceFeature sourceFaceFeature = new FaceFeature();
// 设置人脸特征
sourceFaceFeature.setFeatureData(faceUserInfo.getFaceFeature());
FaceSimilar faceSimilar = new FaceSimilar();
faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
//获取相似值
Integer similarValue = plusHundred(faceSimilar.getScore());
//相似值大于配置预期,加入到识别到人脸的列表
if (similarValue > passRate) {
FaceUserInfo info = new FaceUserInfo();
info.setName(faceUserInfo.getName());
info.setFaceId(faceUserInfo.getFaceId());
info.setSimilarValue(similarValue);
resultFaceInfoList.add(info);
}
}
} catch (Exception e) {
logger.error("", e);
} finally {
if (faceEngine != null) {
faceEngineObjectPool.returnObject(faceEngine);
}
}
return resultFaceInfoList;
}
}
在mapper下SysUserFaceInfoMapper -> 创建接口 getUserFaceInfoByGroupId
/**
* 根据分组Id
* 从数据库中取出人脸库
*
* @param groupId
* @return 人脸库
*/
List<FaceUserInfo> getUserFaceInfoByGroupId(Integer groupId);
sql实现
<resultMap id="userFace2" type="top.yangbuyi.dto.FaceUserInfo">
<id column="id" property="id" javaType="int"/>
<result column="group_id" property="groupId" javaType="java.lang.Integer"/>
<result column="name" property="name" javaType="java.lang.String"/>
<result column="face_id" property="faceId" javaType="String"/>
<result column="face_feature" property="faceFeature"/>
</resultMap>
<
select id = "getUserFaceInfoByGroupId" resultMap="userFace2" parameterType="java.lang.Integer"
resultType="top.yangbuyi.dto.FaceUserInfo">
select id, group_id, face_id, name, face_feature
from `sys_user_face_info`
where group_id = #{groupId}
< /
select>
userFaceInfoService 新增人脸业务接口
/**
* 新增用户人脸识别
*
* @param sysUserFaceInfo 用户人脸识别
* @return 结果
*/
public int insertSysUserFaceInfo(SysUserFaceInfo sysUserFaceInfo);
就是一共简简单单的新增插入数据 这个就不用我教了吧?????????
import top.yangbuyi.domain.AjaxResult;
/**
* (sys_user_face_info)表控制层
*
* @author yangbuyi.top
*/
@RestController
@Slf4j
@RequestMapping("face")
@RequiredArgsConstructor
public class SysUserFaceInfoController {
public final static Logger logger = LoggerFactory.getLogger(SysUserFaceInfoController.class);
private final FaceEngineService faceEngineService;
private final SysUserFaceInfoService userFaceInfoService;
/**
* 人脸添加
*
* @param file 人脸附件
* @param groupId 分组id
* @param name 用户登录名称
*/
@RequestMapping(value = "/faceAdd", method = RequestMethod.POST)
public AjaxResult faceAdd (@RequestBody Map<String, Object> map) {
// 业务.... yangbuyi.top 版权所有
return AjaxResult.success();
}
/**
* 人脸识别
*
* @param file 人脸附件
* @param groupId 分组ID 方便快速识别
*/
@RequestMapping(value = "/faceSearch", method = RequestMethod.POST)
public AjaxResult faceSearch (@RequestBody Map<String, Object> map) throws Exception {
// 业务... yangbuyi.top 版权所有
return AjaxResult.success();
}
- 根据前端传递的
file -> 人脸图片
groupId -> 用户分组(用于缩小范围查询该人员的位置有效减少数据量大的问题)
name -> 当前用户名称(实际开发当中应该是存储id)
String file = String.valueOf(map.get("file"));
String groupId = String.valueOf(map.get("groupId"));
String name = String.valueOf(map.get("name"));
try {
if (file == null) {
return AjaxResult.error("file is null");
}
if (groupId == null) {
return AjaxResult.error("file is null");
}
if (name == null) {
return AjaxResult.error("file is null");
}
// 转换实体
byte[] decode = Base64.decode(base64Process(file));
ImageInfo imageInfo = ImageFactory.getRGBData(decode);
//人脸特征获取
byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
if (bytes == null) {
return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
}
List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
// 开始将人脸识别Base64转换文件流->用于文件上传
// final MultipartFile multipartFile = ImageUtils.base64ToMultipartFile(file);
final SysUserFaceInfo one = this.userFaceInfoService.lambdaQuery().eq(SysUserFaceInfo::getName, name).one();
// 如果存在则更新人脸和特征
if (null != one) {
this.userFaceInfoService.lambdaUpdate().set(SysUserFaceInfo::getFaceFeature, bytes)
.set(SysUserFaceInfo::getFpath, "存储头像地址")
.eq(SysUserFaceInfo::getFaceId, name).update();
} else {
// 组装人脸实体
SysUserFaceInfo userFaceInfo = new SysUserFaceInfo();
userFaceInfo.setName(name);
userFaceInfo.setAge(processInfoList.get(0).getAge());
userFaceInfo.setGender(processInfoList.get(0).getGender().shortValue());
userFaceInfo.setGroupId(Integer.valueOf(groupId));
userFaceInfo.setFaceFeature(bytes);
userFaceInfo.setFpath("存储头像地址");
// 存储用户ID -> 我这里先使用name代替 -> 假如是唯一性
userFaceInfo.setFaceId(name);
//人脸特征插入到数据库
userFaceInfoService.insertSysUserFaceInfo(userFaceInfo);
}
logger.info("faceAdd:" + name);
return AjaxResult.success("人脸绑定成功!");
} catch (Exception e) {
logger.error("", e);
}
// 错误返回
return AjaxResult.error(ErrorCodeEnum.UNKNOWN.getDescription());
- 根据前端传递的
file -> 人脸图片
groupId -> 用户分组(用于缩小范围查询该人员的位置有效减少数据量大的问题)
String file = String.valueOf(map.get("file"));
String groupId = String.valueOf(map.get("groupId"));
if (groupId == null) {
return AjaxResult.error("groupId is null");
}
byte[] decode = Base64.decode(base64Process(file));
BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode));
ImageInfo imageInfo = ImageFactory.bufferedImage2ImageInfo(bufImage);
//人脸特征获取
byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
// 校验是否显示出人脸
if (bytes == null) {
return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
}
//人脸比对,获取比对结果
List<FaceUserInfo> userFaceInfoList = faceEngineService.compareFaceFeature(bytes, Integer.valueOf(groupId));
if (CollectionUtil.isNotEmpty(userFaceInfoList)) {
FaceUserInfo faceUserInfo = userFaceInfoList.get(0);
FaceSearchResDto faceSearchResDto = new FaceSearchResDto();
BeanUtil.copyProperties(faceUserInfo, faceSearchResDto);
List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
if (CollectionUtil.isNotEmpty(processInfoList)) {
//人脸检测
List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
int left = faceInfoList.get(0).getRect().getLeft();
int top = faceInfoList.get(0).getRect().getTop();
int width = faceInfoList.get(0).getRect().getRight() - left;
int height = faceInfoList.get(0).getRect().getBottom() - top;
Graphics2D graphics2D = bufImage.createGraphics();
// 红色
graphics2D.setColor(Color.RED);
BasicStroke stroke = new BasicStroke(5f);
graphics2D.setStroke(stroke);
graphics2D.drawRect(left, top, width, height);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(bufImage, "jpg", outputStream);
byte[] bytes1 = outputStream.toByteArray();
faceSearchResDto.setImage("data:image/jpeg;base64," + Base64Utils.encodeToString(bytes1));
faceSearchResDto.setAge(processInfoList.get(0).getAge());
faceSearchResDto.setGender(processInfoList.get(0).getGender().equals(1) ? "女" : "男");
}
return AjaxResult.success(faceSearchResDto);
}
return AjaxResult.error(ErrorCodeEnum.FACE_DOES_NOT_MATCH.getDescription());
准备两张图片
百度随便搜索个在线转换 Base64
(在线图片转Base64)
新增有脸的
新增测试无脸的
有脸的
无脸的
(前往享受人脸识别)
大家好,又见面了,我是你们的朋友全栈君。目录一、环境准备二、mysql下载和安装三、keepalived下载和安装四、配置mysql双主+keepalived高可用环境五、遇到的问题及解决方法一、环境准备1.节点信息节点IP节点名称系统软件及版本192.168.51.187node187CentOS7keepalived-1.3.5 mysql-5.7.24192.168.51.226node226CentOS72.虚拟VIP虚拟VIP192.168.51.1703.初始化,在两个节点上进行常用工具的安装yuminstallgccgcc-c++vim-enhancedglibcmakeunzipopensslopenssl-developenssh-serveropenssh-clientswget-y复制二、mysql下载和安装*除标明特定节点执行外,其他步骤均在两个节点执行,在两个节点安装mysql1.官网下载链接:https://downloads.mysql.com/archives/community/选择版本并下载2.检查是否已经安装了mysql或者卸载系统自带的Maria
文章目录1.题目2.解题1.题目表:Activity+----------------+---------+ |ColumnName|Type| +----------------+---------+ |machine_id|int| |process_id|int| |activity_type|enum| |timestamp|float| +----------------+---------+复制该表展示了一家工厂网站的用户活动. (machine_id,process_id,activity_type)是当前表的主键. machine_id是一台机器的ID号. process_id是运行在各机器上的进程ID号. activity_type是枚举类型(‘start’,‘end’). timestamp是浮点类型,代表当前时间(以秒为单位).‘start’代表该进程在这台机器上的开始运行时间戳, ‘end’代表该进程在这台机器上的终止运行时间戳. 同一台机器,同一个进程都有一对开始时间戳和结束时间戳,而且开始时间戳永远在结束时间戳前面.现在有一个工厂网站由几台机器运行,每台机器
1.首先在当前应用的按钮的点击事件里面添加如下按钮```Intentintent=newIntent();ComponentNamecn=newComponentName("open_open.com.androidagreedemo","open_open.com.androidagreedemo.MainActivity");try{intent.setComponent(cn);intent.putExtra("test","intent1");startActivity(intent);}catch(Exceptione){Log.i("chargeapp","没有找到对应app");}```2.接着需要将目标Activity的android:exported="true"属性在所属应用AndroidMainfest里设置为true(意思是当前Activity可以被外部应用访问)。3.在当前应用的AndroidMainfest里声明目标Ac
首先,我们先来看本地如何生成图片验证码的,再来写输出到网页的验证码如何实现。先来看最简单的—实现的功能是,将一个字符串变成图片写入到文件中实现代码:packagecn.hncu.img; importjava.awt.Graphics; importjava.awt.image.BufferedImage; importjava.io.FileNotFoundException; importjava.io.FileOutputStream; importjava.io.IOException; importjavax.imageio.ImageIO; //该类包含一些用来查找ImageReader和ImageWriter以及执行简单编码和解码的静态便捷方法。 importorg.junit.Test; publicclassImgDemo{ //学习如何把一个字符串变成图片写到一个文件 @Test publicvoidImgDemo1()throwsFileNotFoundException,IOException{ BufferedImageimg=newBuffered
今天给大家带来一个比较实用的东西,那就是用C语言对电脑的开机密码进行修改,按照正常的方法修改一般会提示你输入原密码,我们今天的方法可以直接修改,话不多说,上代码: #include<stdio.h> #include<stdlib.h> intmain(){ charuser[100]={0}; char*username="用户名"; char*password="密码"; sprintf(user,"netuser%s%s",username,password); system(user); return0; }复制其实这个方法的本质是使用了windows系统命令行的netuser命令,可能我们对Linux命令非常熟悉,但对Windows命令了解用法的很少,netuser在做Windows渗透测试时候会经常用到,这个命令用于创建和修改计算机上的用户帐户,当不带选项使用本命令时,它会列出计算机上的用户帐户。当带选项使用时,如果用户名不存在,则创建一个,而它的修改密码的格式为:netuser用户名密码接
和尚刚学习了Isolate的部分基本用法,今天继续尝试compute及其使用方式;Isolate和尚之前了解到ReceivePort和SendPort是成对出现的,是Isolate之间唯一的消息通讯的方式;ReceivePortabstractclassReceivePortimplementsStream{ externalfactoryReceivePort(); externalfactoryReceivePort.fromRawReceivePort(RawReceivePortrawPort); StreamSubscriptionlisten(voidonData(varmessage), {FunctiononError,voidonDone(),boolcancelOnError}); voidclose(); SendPortgetsendPort; }复制简单分析源码可得,ReceivePort中通过get获取一个SendPort对象,通过SendPort发送消息到ReceivePort中,之后再通过listen进行监听;SendPortabstractcl
前言在WPF/Silverlight当中,如果已经存在的Element无法满足你特殊的需求,你可能想自定义Element,那么就有可能会面临重写MeasureOverride和ArrangeOverride两个方法,而这两个方法是WPF/SL的Layout系统提供给用户的自定义接口,因此,理解Layout系统的工作机制,对自定义Element是非常有必要的。那么,究竟WPF/SL的Layout系统是怎么工作的呢?接下来,我简单的描述一下,然后,在后面的章节具体分析。简单来说,WPF的Layout系统是一个递归系统,他有两个子过程,总是以调用父元素的Measure方法开始,以调用Ararnge方法结束,而进入每个子过程之后,父元素又会调用孩子元素的Measure,完成后,又调用孩子元素的Arrange方法,这样一直递归下去。而对两个子过程的一次调用,可以看作是一次会话,可以理解为下图所示:这个会话可以用下面一段话描述:子过程1:父根据自己的策略给孩子一个availableSize,并发起对话,通过调用孩子的Measure(availableSize)方法,询问孩子:你想要多大的空间显示自
总在同一个地方栽坑里是不可原谅的,以本文做个记录,以防下次再犯。 下面这段很简单的基于双重检查锁定(Double-checkedlocking)实现的延迟初始化(Lazyinitialization)代码,还是让spotbugs找出了问题(感谢spotbugs)。 原因很简单,这种模式在java下无效,因为filedNames变量不是线程可见的,具体原因涉及到java内存模型,网上已经有文章很深入的介绍,参见本文末尾的参考资料4privateList<String>filedNames=null; publicList<String>getFieldNames(){ //Double-checkedlocking if(null==filedNames){ synchronized(this){ if(null==filedNames){ filedNames=doGetFieldNames(); } } } returnfiledNames; }复制解决方案1把fieldName声明为volatile型,其他代码不变。 注意:这个解决方案需要JDK5或更高版本
在一个程序中,如果输入输出比较多,输入之间和输出之间相互制约的条件较多,在这种情况下应用决策表很合适,它可以很清楚地表达它们之间的各种复杂关系 决策表法简述: 决策表是把作为条件的所输入的各种组合值以及对应输出值都罗列出来而形成的表格。它能够将复杂的问题按照各种可能的情况全部列举出来,简明并可以避免遗漏。因此利用决策表能够设计除完整的测试用例集合。 决策表通常由以下4部分组成: 条件桩——列出问题的所有条件 条件项——针对条件桩给出的条件列出所有可能取值 动作桩——列出问题规定的可能采取的操作 动作项——指出在条件项的各组取值情况下应采取的动作 举例: 打印机是否能打印出来正确的内容有多个因素影响,包括驱动程序、纸张、墨粉等。(为了简化问题,不考虑中途断电、卡纸等因素的影响)假定:优先警告缺纸,然后警告没有墨粉,最后警告驱动程序不对。 先考虑等价类怎么做? 决策表 打印机是否能打印出来正确的内容有很多个因素影响,保罗驱动程序、纸张、墨粉等。(为了简化问题,不考虑种土断电、卡纸等因素的影响) 假定:优先警告缺纸,然后警告没有墨粉,最后警告驱动程序不对 1)列出
为了支持SSL/TLS,Java提供了javax.net.sslAPI的类SslContext和SslEngine使它相对简单的实现解密和加密。Netty利用该API实现了ChannelHandler的子类SslHandler。下图为Sslhandler的数据流图: 加密的入站数据被SslHandler拦截,进行解密 数据被解密后,原始数据入站 原始数据经过SslHandler SslHandler加密数据并传递数据出站 下面是SslHandler的一个简单使用: 1publicclassSslChannelInitializerextendsChannelInitializer<Channel>{ 2privatefinalSslContextcontext; 3privatefinalbooleanclient; 4privatefinalbooleanstartTls;//第一次请求是否加密 5 6publicSslChannelInitializer(SslContex
数塔 TimeLimit:1000/1000MS(Java/Others) MemoryLimit:32768/32768K(Java/Others)TotalSubmission(s):18918 AcceptedSubmission(s):11276 ProblemDescription 在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?已经告诉你了,这是个DP的题目,你能AC吗? Input 输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1<=N<=100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。 Output 对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。 Samp
方法就是修改这个文件【jquery.unobtrusive-ajax.js】复制 options.data.push({name:"X-Requested-With",value:"XMLHttpRequest"}); options.context=element;//<---100行附近加上这句代码 method=options.type.toUpperCase(); if(!isMethodProxySafe(method)){ options.type="POST"; options.data.push({name:"X-HTTP-Method-Override",value:method}); }复制 【参考文献】http://stackoverflow.com/questions/6630189/how-to-use-this-inside-mvc3-ajax-actionlink-onbegin-oncomplete-events
原文链接:http://blog.csdn.net/qq_38646470/article/details/79564392 符号表示 |和管道特别形象。 作用: 管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常说的管道多是指无名管道,无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别。 有名管道叫namedpipe或者FIFO(先进先出),可以用函数mkfifo()创建。 实现机制 在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为: 限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的
如果你是一名Web开发人员,那么用膝盖想也知道你的职业生涯大部分将使用Java而度过。这是一款商业级的编程语言,我们没有办法不接触它。 对于Java,有两种截然不同的观点:一种认为Java是最简单功能最强大的编程语言之一,另一种则表示这种编程语言既难用又复杂。 下面这些工具或许功能和作用不同,但是有着一个共同的主旨,那就是——它们都是为了给Java编码和开发提供卓越的支持。 1.JDK(Java开发工具包) 如果你打算用Java开发一些小程序和应用程序,那么首先得给自己准备一个类似于JDK的工具,其中包括必要的JavaComplier、JavaRuntimeEnvironment(JRE)和JavaAPI。这是开始Java之旅的第一步。 官方网站:http://java.com/en/download/faq/develop.xml 2.EclipseIDE 如果咨询一些经验丰富的Java开发人员关于他们最喜欢的JavaIntegratedDevelopmentEnvironment(IDE)是什么,不少人会告诉你是EclipseIDE。Eclipse能提供关于代码完成、重构和语法检
一、实验五网络编程与安全-1 1.实验要求: 两人一组结对编程: (1)参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA; (2)结对实现中缀表达式转后缀表达式的功能MyBC.java; (3)结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java; (4)上传测试代码运行结果截图和码云链接; 实验截图: (二)结对编程 实验要求: 一人负责客户端,一人负责服务器 0.注意责任归宿,要会通过测试证明自己没有问题 1.基于JavaSocket实现客户端/服务器功能,传输方式用TCP 2.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器 3.服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端 4.客户端显示服务器发送过来的结果 5.上传测试结果截图和码云链接 实验截图: (三)加密结对编程 实验要求 加密结对编程:1人负责客户端,一人负责服务器 注意责任归宿,要会通过测试证明自己没有问
Vasyaissittingonanextremelyboringmathclass.Tohavefun,hetookapieceofpaperandwroteoutnnumbersonasingleline.Afterthat,Vasyabegantowriteoutdifferentwaystoputpluses("+")inthelinebetweencertaindigitsinthelinesothattheresultwasacorrectarithmeticexpression;formally,notwoplusesinsuchapartitioncanstandtogether(betweenanytwoadjacentplusestheremustbeatleastonedigit),andnopluscanstandatthebeginningortheendofaline.Forexample,inthestring100500,ways100500(addnopluses),1+00+500or10050+0arecorrect,andways100++500
内容概要 1登录注册模态框分析 1.1Login.vue 1.2Header.vue 2登录注册前端页面复制 2.0Header.vue 2.1Login.vue 2.3Register.vue 3腾讯短信功能二次封装 3.2封装v3版本 ***3.2.2把发送短信封装成包*** 4.短信验证码接口 5.短信登录接口 6.短信注册接口 内容详细 1登录注册模态框分析 #如果是跳转新的页面 -路由中配置一个路由 -写一个视图组件 #弹窗,盖在主页上---》模态框 复制 Login.vue <template> <divclass="login"> <span@click="handleClose">X</span> </div> </template> <script> exportdefault{ name:"Login", methods:{ handleClose(){ this.$emit('close') } } } </script> &
在实际项目中,我们难免会遇到一些无值。当我们转JSON时,不希望这些null出现,比如我们期望所有的null在转JSON时都变成“”“”这种空字符串,那怎么做呢? Jackson中对null的处理 1@Configuration 2publicclassJacksonConfig{ 3@Bean 4@Primary 5@ConditionalOnMissingBean(ObjectMapper.class) 6publicObjectMapperjacksonObjectMapper(Jackson2ObjectMapperBuilderbuilder){ 7ObjectMapperobjectMapper=builder.createXmlMapper(false).build(); 8objectMapper.getSerializerProvider().setNullValueSerializer(newJsonSerializer<Object>(){ 9@Override 10publicvoidserialize(Objecto,JsonGeneratorj