0%

文章字数:43,阅读全文大约需要1分钟

报错 org.springframework.orm.jpa.JpaSystemException: Null value was assigned to a property[class xxx.xxx.xxx] of primitive type setter of...

报错原因

jpa实体属性为int类型,但是数据库中的数据为空。无法为属性赋值,所以报错


文章字数:136,阅读全文大约需要1分钟
OpenResty 的目标是让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL,PostgreSQL,~Memcaches 以及 ~Redis 等都进行一致的高性能响应。

所以对于一些高性能的服务来说,可以直接使用 OpenResty 访问 Mysql或Redis等,而不需要通过第三方语言(PHP、Python、Ruby)等来访问数据库再返回,这大大提高了应用的性能。


文章字数:23,阅读全文大约需要1分钟

java.util.Observable

ApplicationEventPublisher
@EventListener

发布订阅

applicationContext.publishEvent(MessData data);// 发送

@EventListener
public void hander(MessData data){}//订阅MessData相关的


文章字数:20,阅读全文大约需要1分钟

作用

REST风格接口参数获取

代码

1
2
3
4
5
6
@RequestMapping("/testPathVariable/{id}")
    public String testPathVariable(@PathVariable("id") Integer id)
    {
        System.out.println("testPathVariable:"+id);
        return SUCCESS;
    }

文章字数:361,阅读全文大约需要1分钟

非对称加密,公钥可以加密,私钥才能解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSAEncrypt {
private static Map<Integer, String> keyMap = new HashMap<Integer, String>(); //用于封装随机产生的公钥与私钥
public static void main(String[] args) throws Exception {
//生成公钥和私钥
genKeyPair();
System.out.println("公钥:" + keyMap.get(0));
System.out.println("私钥:" + keyMap.get(1));
//加密字符串
String message = "test123";
String messageEn = encrypt(message,keyMap.get(0));
System.out.println(message + "\t加密后的字符串为:" + messageEn);
String messageDe = decrypt(messageEn,keyMap.get(1));
System.out.println("还原后的字符串为:" + messageDe);
}

/**
* 随机生成密钥对
* @throws NoSuchAlgorithmException
*/
public static void genKeyPair() throws NoSuchAlgorithmException {
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器,密钥大小为96-1024位
keyPairGen.initialize(1024,new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
// 将公钥和私钥保存到Map
keyMap.put(0,publicKeyString); //0表示公钥
keyMap.put(1,privateKeyString); //1表示私钥
}
/**
* RSA公钥加密
*
* @param str
* 加密字符串
* @param publicKey
* 公钥
* @return 密文
* @throws Exception
* 加密过程中的异常信息
*/
public static String encrypt( String str, String publicKey ) throws Exception{
//base64编码的公钥
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}

/**
* RSA私钥解密
*
* @param str
* 加密字符串
* @param privateKey
* 私钥
* @return 铭文
* @throws Exception
* 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception{
//64位解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}

}

文章字数:496,阅读全文大约需要1分钟

安全原则

  1. 最小权限原则:将角色配置成其完成任务所需的最小权限集合
  2. 责任分离原则:可以通过相互独立互斥的角色共同完成敏感任务,例如记账员和财务管理员共同完成过账操作
  3. 数据抽象原则:通过权限的抽象来体现,例如借款,存款,等,而不是使用读、写、执行权限

组成

  1. User用户:每个用户有唯一的UUID标识,并被授予不同角色
  2. Role角色:每个角色有不同的权限
  3. Permission权限:访问权限
  4. User-Role用户权限映射
  5. Role-Permission角色-权限映射

RBAC0

1
2
3
4
5
6
7
用户 <--- 用户角色分配 ----> 角色 <--- 角色权限分配 ---> (操作 <--- 权限 ---> 控制对象)
^
\ /
和用户联系的会话 激活的角色
\ /
\ /
用户会话集合
  1. 用户和角色为多对多关系
  2. 每个用户至少有一个角色
  • 优点是结构简单,灵活授权

RBAC1

  • RBAC0基本一致,角色改为树形结构,可以继承

RBAC2

  • 增加一下约束
  1. 互斥角色:同一用户智能分配到一组互斥角色集合至多一个角色。对于某一个用户,某一次活动中只能有一个角色。
  2. 基数约束:角色分配数量受限、用户拥有角色数目受限、角色访问权限数目受限等
  3. 先决条件角色:拥有某一基础角色权限后,才能获取更高级别的权限
  4. 运行互斥:允许用户获取两个角色,但运行中不能两个角色同时激活

文章字数:212,阅读全文大约需要1分钟

redis和消息队列的区别在于消息队列拥有更完善的功能,redis是可以加入一些功能实现消息队列的

容错机制

容错机制需要保证出错后信息还会被重新插入队列,在redis中需要手动try catch处理。在其他消息队列中都有提供容错机制,比如消息取出变为挂起状态,一定时间内没有删除就会重新插入队列。

消息延迟

消息队列中有延迟的概念,取出后判断是否在延迟时间内,如果在则重新插入队列,达到延迟效果。

推送

redis的推送方式注重快速而不保证推送成功
部分MQ可以做到虽然有延迟,但是保证推送成功。


文章字数:538,阅读全文大约需要2分钟

Reactor pattern(反应器模式)是一种处理并发服务器请求,将请求提交给一个或者多个服务器处理程序的事件设计模式。机制是客户端使用一个线程监听连接请求,监听到后提交给另一个专门负责定时批量处理请求的非阻塞线程。node.js,netty等均使用这种设计模式实现

传统方法(thread-based architecture基于线程)

传统的应对服务器并发请求的方法是

  1. 死循环监听服务器请求
  2. 接收到请求开启一个线程处理此请求

这样带来了几个后果

  1. 产生大量线程,线程间切换需要消耗资源。
  2. 大量线程占用内存空间
  3. 处理请求的线程大部分时间用于等待输入

event-driven architecture(事件驱动)方法

Reactor设计模式就是事件驱动方法的实现之一。监听请求并转发给对应的事件处理器来执行。
组成角色

  1. handle(句柄/文件描述符)
    事件的发源地,如socket、磁盘文件。
    handle可能会发生的事件有:connection、ready for read、ready for write等。
  2. Synchronous Event Demultiplexer(同步事件分离器)
    阻塞等待Handles中的事件发生。
  3. Event Handler(事件处理器的接口)
  4. Concrete Event Handler(事件处理器实现)
    事件处理器可能有多种,所以声明接口,使用接口调用。不同类型事件处理器具体实现不同。

和观察者模式对比

  1. 都是主体发生变化时通知依附的客体。

  2. 观察者模式是与单个事件源有关(发布),反应器模式与多个事件源关联。


文章字数:294,阅读全文大约需要1分钟

org.springframework.data.redis.core.RedisTemplate

依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0

注入Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//使用fastjson序列化
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
@Component
public class RedisService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 默认过期时长,单位:秒
*/
public static final long DEFAULT_EXPIRE = 60 * 60 * 24;

/**
* 不设置过期时长
*/
public static final long NOT_EXPIRE = -1;

public boolean existsKey(String key) {
return redisTemplate.hasKey(key);
}

/**
* 重名名key,如果newKey已经存在,则newKey的原值被覆盖
*
* @param oldKey
* @param newKey
*/
public void renameKey(String oldKey, String newKey) {
redisTemplate.rename(oldKey, newKey);
}

/**
* newKey不存在时才重命名
*
* @param oldKey
* @param newKey
* @return 修改成功返回true
*/
public boolean renameKeyNotExist(String oldKey, String newKey) {
return redisTemplate.renameIfAbsent(oldKey, newKey);
}

/**
* 删除key
*
* @param key
*/
public void deleteKey(String key) {
redisTemplate.delete(key);
}

/**
* 删除多个key
*
* @param keys
*/
public void deleteKey(String... keys) {
Set<String> kSet = Stream.of(keys).map(k -> k).collect(Collectors.toSet());
redisTemplate.delete(kSet);
}

/**
* 删除Key的集合
*
* @param keys
*/
public void deleteKey(Collection<String> keys) {
Set<String> kSet = keys.stream().map(k -> k).collect(Collectors.toSet());
redisTemplate.delete(kSet);
}

/**
* 设置key的生命周期
*
* @param key
* @param time
* @param timeUnit
*/
public void expireKey(String key, long time, TimeUnit timeUnit) {
redisTemplate.expire(key, time, timeUnit);
}

/**
* 指定key在指定的日期过期
*
* @param key
* @param date
*/
public void expireKeyAt(String key, Date date) {
redisTemplate.expireAt(key, date);
}

/**
* 查询key的生命周期
*
* @param key
* @param timeUnit
* @return
*/
public long getKeyExpire(String key, TimeUnit timeUnit) {
return redisTemplate.getExpire(key, timeUnit);
}
/**
* 将key设置为永久有效
*
* @param key
*/
public void persistKey(String key) {
redisTemplate.persist(key);
}
}

文章字数:54,阅读全文大约需要1分钟

RedisTemplate每次都从连接池中获取一个连接,并执行回调。执行完毕后将连接放回连接池。

源码

基于jdk1.8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = this.getConnectionFactory();
RedisConnection conn = null;

Object var11;
try {
if (this.enableTransactionSupport) {
conn = RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}

boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}

RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
T result = action.doInRedis(connToExpose);
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}

var11 = this.postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}

return var11;
}