1 传统垂直应用架构

以经典的 MVC 垂直架构为例,通常分三层:

  1. View:视图展示层,使用 JSP/JS/HTML+CSS。
  2. Controller:调度控制层,请求的分发。
  3. Model:应用模型层,业务数据和业务执行逻辑,被多个视图重用。

标准的 MVC 模式并不包含数据访问层,但实际开发中需要专门的数据库连接池和统一的数据库访问接口对接数据库,于是 ORM 框架逐渐流行起来。

阅读全文 »

1. bash 手册

用于查看命令的具体详情

man xxx

2. ls 文件和目录列表

-a 显示隐藏文件
文件名支持 *? 符号过滤

3. 处理文件

-i 询问参数
touch
cp file1 file2:复制文件。参数 -R 用于递归复制文件
mv file1 file2:移动文件
rm file1:删除文件,文件名支持?*

阅读全文 »

1. 第一个面试大概是年前还差半个月放假的某天晚上,一个电话面试,杭州的一个偏传统公司,应该是主要做 ERP 这类的系统,最后 HR 等年后通知就没通知了。

主要问了 java 集合,包括里面 List、Set、Map 各个区别以及相关算法、Spring 的设计模式、Spring MVC 和 Servlet 的联系区别、Spring 的相关源码,我当时只说了 Spring MVC 启动、运行源码流程,@Autowire 注入的时候的 AutowireAnnotationBeanPostProcessor 的相关逻辑,顺带讲了 Bean 的生命周期,循环注入等,还有一些常见的面试题给忘了。

阅读全文 »

1. Thread:ThreadLocalMap = 1:1

每个 Thread 内部维护了一个 ThreadLocal.ThreadLocalMap 对象

2. ThreadLocalMap:[ThreadLocal, Entry] = 1:16

每个 ThreadLocalMap 内部维护的键值对是 [ThreadLocal, Entry]。
而在底层,查找的时候是通过 ThreadLocal.threadLocalHashCode & (table.length - 1) 获取值,得到 0~15 之间的值,并获取到 Entry[i],进而获取 Entry.value。

阅读全文 »

1. Spring 的核心

1. IOC/DI(控制反转/依赖注入)

注入方式: set方法、构造器、工厂方法

2. AOP(面向切面编程)

使用的是动态代理,通过 JDK(接口实现) 或 CGLib 字节码工具包的继承来实现动态代理

2. Bean 的生命周期

1. 实例化 Bean

对于 BeanFactory 容器,是懒实例;而 ApplicationiContext 容器则是容器启动时就会实例化所有的 Bean

阅读全文 »

java7 的 ConcurrentHashMap

在构造函数就初始化了 Segment
使用 Segment 可重入分段锁 + 链表结构的 HashEntry

Segment

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
static final class Segment<K,V> extends ReentrantLock implements Serializable {

private static final long serialVersionUID = 2249069246763182397L;
// 如果是多处理器则是 64,否则是 1 次
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
// put 的时候,通过 scanAndLockForPut 自旋锁,for 循环 64/1 次尝试获取锁,如果一直没获取到,则 lock() 将自己挂起,然后等待 put 完之后的 unlock() 将自己唤醒
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}
}
public int size() {
final Segment<K,V>[] segments = this.segments;
int size;
boolean overflow;
long sum;
long last = 0L;
int retries = -1;
try {
// 获取 size 时,先进行三次无锁 CAS 的合并计算,如果前后两次相同则返回结果,如果前后两次都不一样则再对每个 Segment 加锁后获取 size 进行合并
for (;;) {
if (retries++ == RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
ensureSegment(j).lock();
}
sum = 0L;
size = 0;
overflow = false;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> seg = segmentAt(segments, j);
if (seg != null) {
sum += seg.modCount;
int c = seg.count;
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
if (sum == last)
break;
last = sum;
}
} finally {
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}
阅读全文 »

1. web.xml 的加载顺序

ServletContext -> context-param -> listener -> filter -> servlet。
前面的两个用于在 web.xml 设置变量,重要的是后面三个,即 Listener、Filter、Servlet,为 javaweb 的三大组件。

阅读全文 »