为什么Spring官方推荐通过构造器注入

我们在使用Spring的时候,最方便的就是它的IOC(控制反转),也就是所有的Bean都交由

Spring进行管理,那么我们在看网上的文章或者自己在写代码的时候经常会像这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class TestService {

@Autowired
TestDao TestDao;

public void getInfo(){
}
}

@Service
public class TestService {

private final TestDao TestDao;

@Autowired
public TestService(TestDao TestDao){
this.TestDao = TestDao;
}

public void getInfo(){
}

}

但是貌似许多人都会使用第一种方式,因为这样简单方便,如果是采用第二种的话,每一次新增加一个bean,都需要在构造器的入参上面加一个参数,就会显得有点麻烦。

阅读更多

JDK1.8下ConcurrentHashMap的一些理解(一)

在JDK1.8里面,ConcurrentHashMap在put方法里面已经将分段锁移除了,转而是CAS锁和synchronized

ConcurrentHashMap是Java里面同时兼顾性能和线程安全的一个键值对集合,同属于键值对的集合还有HashTable以及HashMap
HashTable是一个线程安全的类,因为它的所有public方法都被synchronized修饰,这样就导致了一个问题,就是效率太低。

虽然HashMapJDK1.8的并发场景下触发扩容时不会出现成环了,但是会出现数据丢失的情况。
所以如果需要在多线程的情况下(多读少写))使用Map集合的话,ConcurrentHashMap是一个不错的选择。

ConcurrentHashMap在JDK1.8的时候将put()方法中的分段锁Segment移除,转而采用一种CAS锁和synchronized来实现插入方法的线程安全。
如下代码:

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
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
//省略相关代码
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
//省略相关代码
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
阅读更多

JDK1.7和1.8中的HashMap区别

Jdk1.7和1.8中,HashMap的一些关键点几乎重写了。

主要变更点:

1. hash扰动算法

在jdk1.7的时候,HahMap的hash扰动算法如下:

1
2
3
4
5
6
7
8
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}

阅读更多

Java集合学习之HashSet

简介

在一般的使用中,HashSet经常用于数据的去重,例如我们有一个List,这个List里面有一些重复的数据,于是我们便可以这样操作

1
2
3
4
5
6
7
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("a");
Set<String> set =new HashSet<>();
set.addAll(list);

此时,在Set里面,只会有一个a元素。

底层

阅读更多

Spring中AOP的探索与实践(二)之Mongo多数据源切换

在之前的一片文章中介绍了使用AOP的方式来实现Redis的多数据源切换。而今天这一篇则是主要讲述Mongo的多数据源切换。

使用AOP来实现Mongo的数据源切换与Redis的AOP切换相同,不同之处是需要替换MongoRepository里面的MongoOperations,从而实现多数据源的切换

代码示例

配置类,读取Mongo的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class MongoMultiProperties {

private static final Logger LOGGER = LoggerFactory.getLogger(MongoMultiProperties.class);

@Bean(name="mongodb1")
@Primary
@ConfigurationProperties(prefix = "spring.data.mongodb.db1")
public MongoProperties db1Properties(){
LOGGER.info("正在初始化db1");
return new MongoProperties();
}

@Bean(name = "mongodb2")
@ConfigurationProperties(prefix = "spring.data.mongodb.db2")
public MongoProperties db2Properties(){
LOGGER.info("正在初始化db2");
return new MongoProperties();
}

}
阅读更多

Spring中AOP的探索与实践(一)之Redis多数据源切换

一般在项目的使用过程中,有时候为了减轻数据库的压力,从而将一部分数据缓存至Redis,但是随着业务量的增多。我们所需要的Redis服务器也会越来越多,就算不需要多个Redis数据源,那么在一个redis里面,切换不同的DB也是很麻烦的一件事情。

非AOP的一般的多数据源操作

在Redis的多数据源使用中,一般的方法是从配置文件中读取多个RedisProperties,读取到配置文件之后,将RedisProperties配置到RedisTemplate,然后每次使用的时候就通过不同的Template来调用Redis服务器。示例如下:

代码示例

Redis的配置类

阅读更多

java异步IO的回调机制

异步

在Java的nio里面,经常遇到的一个词语是回调,一个主线程负责分发各种请求至子线程,同时子线程处理完毕之后通知主线程,这其中就涉及到了回调机制。

在Java中,异步IO的回调方式主要是CallBackFuture

由于Future获取结果是一种阻塞的方式,所以本次就主要来了解Callback回调方式的运行机制。

由于在异步IO里面,主线程不需要等待子线程来获取结果,所以可以极大的提高程序运行的效率,但是子线程必须在完成之后通知父线程,于时这就引出了回调。

阅读更多

SSO单点登录的理论与实践(一)

SSO简介

随着企业业务的发展,企业中可能会出现多个业务的系统。如果是对内使用的话,那稍微还好点,如果是ToC的业务,客户如果每进入一个系统都需要登录的话,对用户来说是一个麻烦事情,很可能会造成用户的流失。

如果在每一个系统里面都存储一份用户的账号和密码数据,这种做法显然是不靠谱的,而且也不安全的。会造成客户的数据大量冗余,而且还会导致后期维护十分的麻烦。

所以,现在一般都会采用SSO单点登录

在介绍SSO登录之前,需要先了解一下浏览器的同源策略

阅读更多

在Spring中全局处理异常

随着业务的发展,在每一个业务模块里面都可能会出现一些自定义的通用异常,例如·验证失败权限不足等等 ,这些自定义的异常很可能会被所有模块公用,在代码里面,最常见的一种写法是在每一个方法里面进行捕获,然后在Controller里面进行catch,最后进行相应处理

常见写法

第一种写法:

这是一个常规的写法,每一个方法都处理自己的特定异常
Controller层

1
2
3
4
5
6
7
8
9
10
11
public void login(){
try{
//逻辑处理
}catch(AuthException e){
XXX
}catch(CrsfException e){
XXX
}catch(Exception e){
XXX
}
}
阅读更多

Springboot中使用Mysql多数据源

随着业务的发展,很可能需要在一个项目里面同时使用多个数据源。

大致看了网上的多数据源Demo,发现无非有两种:

一种是自己封装多个JdbcTemplate,然后调用对应的数据库就使用对应的JdbcTemplate

一种是通过注解的方式来实现,在需要切换数据源的方法上添加一个自己封装的注解便可以完成切换。

考虑了一下以后的扩展性和通用性,便决定采用基于注解的多数据源方式

阅读更多