This is a record of my craking process for Leetcode.

Word Break ()#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
HashSet<String> set = new HashSet<>(wordDict);
if(s.length() == 0) return set.contains(s);
boolean[] dp = new boolean[s.length()];
for(int i = 0; i < s.length(); i++){
if(set.contains(s.substring(0, i + 1))){
dp[i] = true;
continue;
}
int tmp = 0;
while(tmp < i){
if(dp[tmp] && set.contains(s.substring(tmp + 1, i + 1))){
dp[i] = true;
break;
}
tmp++;
}
if(dp[i]) continue;
dp[i] = false;
}
return dp[s.length() - 1];
}
}

Unique Binary Search Trees II()#

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
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<TreeNode> generateTrees(int n) {
Map<Pair<Integer, Integer>, List<TreeNode>> memo = new HashMap<>();
return generateTreesWithRoot(1, n, memo);
}
public List<TreeNode> generateTreesWithRoot(int l, int r, Map<Pair<Integer, Integer>, List<TreeNode>> memo) {
List<TreeNode> res = new ArrayList<>();
if(l > r) {
res.add(null);
return res;
}

if(memo.containsKey(new Pair<>(l, r))) {
return memo.get(new Pair<>(l, r));
}

for(int i = l; i <= r; i++) {
List<TreeNode> leftSubTrees = generateTreesWithRoot(l, i - 1, memo);
List<TreeNode> rightSubTrees = generateTreesWithRoot(i + 1, r, memo);
for (TreeNode left: leftSubTrees) {
for (TreeNode right: rightSubTrees) {
TreeNode root = new TreeNode(i, left, right);
res.add(root);
}
}
}
memo.put(new Pair<>(l, r), res);
return res;
}
}

Number of Music Playlists()#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int numMusicPlaylists(int n, int goal, int k) {
int MOD = 1_000_000_007;

// Initialize the DP table
long[][] dp = new long[goal + 1][n + 1];
dp[0][0] = 1;

for (int i = 1; i <= goal; i++) {
for (int j = 1; j <= Math.min(i, n); j++) {
// The i-th song is a new song
dp[i][j] = dp[i - 1][j - 1] * (n - j + 1) % MOD;
// The i-th song is a song we have played before
if (j > k) {
dp[i][j] = (dp[i][j] + dp[i - 1][j] * (j - k)) % MOD;
}
}
}

return (int) dp[goal][n];
}
}

Search in a 2D matrix()#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int left = 0, right = m * n - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
int mid_row = mid / n;
int mid_col = mid - mid_row * n;

if(matrix[mid_row][mid_col] == target) {
return true;
}
if(matrix[mid_row][mid_col] < target){
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}

This is a record of my craking process for Leetcode.

1. Number of Ways to Paint N × 3 Grid(#1411)#

1.1 Soulution#

Considering the base case (1 * 3) There are several possible choices. 121 212 213 232 312 313 (1 2 3 represents RGB 3 different colors). Then we can consider the next following posible choice in (2 * 3)

Let 121 represents head and tail have the same color and the middle pos varies

Let 123 represent every pos has different color

Then we can derive the following fomular
$$b_{121} = a_{121} * 3 + a_{123} * 2$$
$$b_{123} = a_{121} * 2 + a_{123} * 2$$

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public int numOfWays(int n) {
long a121 = 6, a123 = 6, b121, b123, mod = (long)1e9 + 7;
for (int i = 1; i < n; ++i) {
b121 = a121 * 3 + a123 * 2;
b123 = a121 * 2 + a123 * 2;
a121 = b121 % mod;
a123 = b123 % mod;
}
return (int)((a121 + a123) % mod);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
impl Solution {
pub fn num_of_ways(n: i32) -> i32 {
let (mut r, mut s) = (6_i64, 6_i64) ;
let x = 10_i64.pow(9) + 7;
for i in 1..n {
let _r = r * 3 + s * 2;
let _s = r * 2 + s * 2;
r = _r % x;
s = _s % x;
}
((r + s) % x) as i32
}
}

2. Robot Bounded In Circle (#1041)#

2.1 Soulution#

The trick in this problem is to consider the impossible cases (The robot does not come back to original nor the bot face the initial position after iteration)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public boolean isRobotBounded(String instructions) {
int[][] directions = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int x = 0, y = 0;
int idx = 0;
for (char i : instructions.toCharArray()) {
if (i == 'L')
idx = (idx + 3) % 4;
else if (i == 'R')
idx = (idx + 1) % 4;
else {
x += directions[idx][0];
y += directions[idx][1];
}
}
return (x == 0 && y == 0) || (idx != 0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl Solution {
pub fn is_robot_bounded(instructions: String) -> bool {
let mut d = (0, 1);
let mut p = (0, 0);
for c in instructions.chars() {
match c {
'G' => p = (p.0 + d.0, p.1 + d.1),
'L' => d = (-d.1, d.0),
'R' => d = (d.1, -d.0),
_ => {}
}
}
p == (0, 0) || d != (0, 1)
}
}

This is a record of my craking process for Leetcode.

1. Leftmost Column with at Least a One (#1428)#

1.1 Soulution1#

The most simple way is two use brute-force (which takes $n^2$ time) But we can optimize it with binary-search

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int leftMostColumnWithOne(BinaryMatrix binaryMatrix) {
int rows = binaryMatrix.dimensions().get(0);
int columns = binaryMatrix.dimensions().get(1);
int res = columns;
for(int row = 0; row < rows; row++) {
int lo = 0;
int hi = columns - 1;
while(lo < hi) {
int mid = lo + (hi - lo) / 2;
if(binaryMatrix.get(row, mid) == 0) {
lo = mid + 1;
} else {
hi = mid;
}
}
if(binaryMatrix.get(row, lo) == 1) {
res = Math.min(lo, res);
}
}
return res == columns ? -1 : res;
}
}
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
use std::cmp;
impl Solution {
pub fn left_most_column_with_one(binaryMatrix: &BinaryMatrix) -> i32 {
let rows = binaryMatrix.dimensions()[0];
let cols = binaryMatrix.dimensions()[1];
let mut res = cols;
for row in (0..rows) {
let mut lo = 0;
let mut hi = cols - 1;
while(lo < hi) {
let mid = lo + (hi - lo) / 2;
if(binaryMatrix.get(row, mid) == 0) {
lo = mid + 1;
}else {
hi = mid;
}
}
if(binaryMatrix.get(row, lo) == 1) {
res = cmp::min(res, lo);
}
}
if(res == cols){
-1
} else{
res
}
}
}

1.1 Soulution2#

An alternate way is doing one loop (going thorugh the two-dimension table) starting from right-top corner.

When we reach a ‘1’ we choose to go left otherwise go down

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int leftMostColumnWithOne(BinaryMatrix binaryMatrix) {
List<Integer> dimensions = binaryMatrix.dimensions();
int r = dimensions.get(0) - 1;
int c = dimensions.get(1) - 1;

while (r >= 0 && c >= 0) {
if (binaryMatrix.get(r, c) == 0) {
r--;
} else {
c--;
}
}

if (c == dimensions.get(1) - 1) {
return -1;
}
return c + 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::cmp;
impl Solution {
pub fn left_most_column_with_one(binaryMatrix: &BinaryMatrix) -> i32 {
let rows = binaryMatrix.dimensions()[0];
let cols = binaryMatrix.dimensions()[1];
let mut current_row = 0;
let mut current_col = cols - 1;
while(current_row < rows && current_col >= 0) {
if(binaryMatrix.get(current_row, current_col) == 0) {
current_row += 1;
} else {
current_col -= 1;
}
}
if(current_col == cols - 1) {
-1
} else {
current_col + 1
}
}
}

2. Move Zeroes (#283)#

2.1 Soulution1#

A very straight-forward 2-point method

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public void moveZeroes(int[] nums) {
for(int p = 0, q = 0; p < nums.length; p++) {
if(nums[p] != 0) {
int temp = nums[p];
nums[p] = nums[q];
nums[q] = temp;
q++;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl Solution {
pub fn move_zeroes(nums: &mut Vec<i32>) {
let mut i = 0;
let mut j = 0;
while i < nums.len(){
if nums[i] != 0{
let temp: i32 = nums[j];
nums[j] = nums[i];
nums[i] = temp;
j += 1;
}
i += 1;
}
}
}

This is a record of my craking process for Leetcode.

1. Pairs of Songs With Total Durations Divisible by 60 (#1010)#

1.1 Problem Description#

You are given a list of songs where the ith song has a duration of time[i] seconds.

Return the number of pairs of songs for which their total duration in seconds is divisible by 60. Formally, we want the number of indices i, j such that i < j with (time[i] + time[j]) % 60 == 0.

Example 1:

Input: time = [30,20,150,100,40]

Output: 3

Explanation: Three pairs have a total duration divisible by 60:

(time[0] = 30, time[2] = 150): total duration 180

(time[1] = 20, time[3] = 100): total duration 120

(time[1] = 20, time[4] = 40): total duration 60

Example 2:

Input: time = [60,60,60]

Output: 3

Explanation: All three pairs have a total duration of 120, which is divisible by 60.

1.2 Soulution#

The most straight-forward way to solve this problem is using two loops which takes $n^2$ time. To optimize it, we can use the same way with two sum, but do a small modification. Since we only need to output the pairs, we use an array (size of 60) to record the current value % 60. Then while in the for-loop, we can check 60 - current_value % 60, get its occurences.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int numPairsDivisibleBy60(int[] time) {
int[] remains = new int[60];
int count = 0;
for(int t : time) {
if (t % 60 == 0) {
count += remains[0];
} else {
count += remains[60 - t % 60];
}
remains[t % 60]++;
}
return count;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl Solution {
pub fn num_pairs_divisible_by60(time: Vec<i32>) -> i32 {
let mut remains = vec![0;60];
let mut count = 0;
for t in time.iter() {
if (t % 60 == 0) {
count += remains[0];
} else {
count += remains[(60 - t % 60) as usize];
}
remains[(t % 60) as usize] += 1
}
count
}
}

Here is my personal notes for Rust

mutable ref and immutable ref#

one memory block can only have one mutable ref but it can have multiple immutable ref. Also, once a memory block has one mutable ref, all the immutable refs before that would be invalid.
For my understanding:
Mutable ref is like a “read and write” operation to memory block, while immutable ref is like a “read only” operation to the memory block. Once you have a “read and write” operation(mutable ref), all other “read only” operation(mutable ref) don’t know what happened to that memory block(might be changed by mutable block), so, the result from “read only” operation may not be valid. However, we have to re-create mutable refs.~

Jvm#

Java 体系结构#

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTIzNTExNTAxLnBuZw

Java 的整体结构#

  1. HotSpot VM是目前市面上高性能虚拟机的代表作之一。
  2. 它采用解释器与即时编译器并存的架构
  3. 在今天,Java程序的运行性能早已脱胎换骨,已经达到了可以和C/C++程序一较高下的地步。
  4. 执行引擎包含三部分:解释器,即时编译器,垃圾回收器

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTMyMzMwNjgyLnBuZw

Java 代码执行流程#

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTMyMzM5MjQ2LnBuZw

JVM 架构模型#

Java编译器输入的指令流基本上是一种基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构。具体来说:这两种架构之间的区别:

基于栈的指令集架构#

  1. 设计和实现更简单,适用于资源受限的系统;
  2. 避开了寄存器的分配难题:使用零地址指令方式分配
  3. 指令流中的指令大部分是零地址指令,其执行过程依赖于操作栈。指令集更小,编译器容易实现
  4. 不需要硬件支持,可移植性更好,更好实现跨平台

基于寄存器的指令级架构#

  1. 典型的应用是x86的二进制指令集:比如传统的PC以及Android的Davlik虚拟机。
  2. 指令集架构则完全依赖硬件,与硬件的耦合度高,可移植性差
  3. 性能优秀和执行更高效
  4. 花费更少的指令去完成一项操作
  5. 在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主

反编译字节码文件#

1
javap -v StackStruTest.class
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
  public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_2 // 将常量 2 压入栈中
1: istore_1 // 将常量 2 保存至变量 i 中
2: iconst_3 // 将常量 3 压入栈中
3: istore_2 // 将常量 3 保存至变量 j 中
4: iload_1 // 加载变量 i
5: iload_2 // 加载变量 j
6: iadd // 执行累加操作
7: istore_3 // 加法结果保存在变量 k 中
8: return
LineNumberTable:
line 10: 0
line 11: 2
line 12: 4
line 22: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
2 7 1 i I
4 5 2 j I
8 1 3 k I
}

JVM 生命周期#

虚拟机的启动#

Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的

虚拟机的执行#

  1. 一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序
  2. 程序开始执行时他才运行,程序结束时他就停止
  3. 执行一个所谓的Java程序的时候,真真正正在执行的是一个叫做Java虚拟机的进程

虚拟机的退出#

  1. 程序正常执行结束 //jps 查看进程
  2. 程序在执行过程中遇到了异常或错误而异常终止
  3. 由于操作系统用现错误而导致Java虚拟机进程终止
  4. 某线程调用Runtime类或System类的exit()方法,或Runtime类的halt()方法,并且Java安全管理器也允许这次exit()或halt()操作。
  5. 除此之外,JNI(Java Native Interface)规范描述了用JNI Invocation API来加载或卸载 Java虚拟机时,Java虚拟机的退出情况。

类加载子系统#

完整框图

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTQ1MzIxMjIyLnBuZw

  1. 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识。

  2. ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。

  3. 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MDgxODEzNDA5LnBuZw

class –> Java.lang.Class

  1. class file存在于本地硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载到JVM当中来根据这个文件实例化出n个一模一样的实例。

  2. class file加载到JVM中,被称为DNA元数据模板,放在方法区

  3. 在.class文件–>JVM–>最终成为元数据模板,此过程就要一个运输工具(类装载器Class Loader),扮演一个快递员的角色。

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MDgxOTEzNTM4LnBuZw

类的加载过程#

类加载过程概述#

1
2
3
4
5
6
public class HelloLoader {
public static void main(String[] args) {
System.out.println("谢谢ClassLoader加载我....");
System.out.println("你的大恩大德,我下辈子再报!");
}
}

类的加载过程

  • 执行 main() 方法(静态方法)就需要先加载承载类 HelloLoader

  • 加载成功,则进行链接、初始化等操作,完成后调用 HelloLoader 类中的静态方法 main

  • 加载失败则抛出异常

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MDgyMjU1NzQ2LnBuZw

  • 完整的流程图如下所示:加载 –> 链接(验证 –> 准备 –> 解析) –> 初始化

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MDgyNjAxNDQxLnBuZw

加载流程#

  1. 通过一个类的全限定名获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

加载class文件的方式#

  1. 从本地系统中直接加载
  2. 通过网络获取,典型场景:Web Applet
  3. 从zip压缩包中读取,成为日后jar、war格式的基础
  4. 运行时计算生成,使用最多的是:动态代理技术
  5. 由其他文件生成,典型场景:JSP应用从专有数据库中提取.class文件,比较少见
  6. 从加密文件中获取,典型的防Class文件被反编译的保护措施

链接阶段#

链接分为三个子阶段:验证 –> 准备 –> 解析

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTU0MDM5NjIwLnBuZw验证

  1. 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全
  2. 主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

举例

  • 使用 BinaryViewer 查看字节码文件,其开头均为 CAFE BABE ,如果出现不合法的字节码文件,那么将会验证不通过

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTU0NjUxOTY1LnBuZw

准备

  1. 为类变量分配内存并且设置该类变量的默认初始值,即零值
  2. 这里不包含用final修饰的static,因为final在编译的时候就会分配好了默认值,准备阶段会显式初始化
  3. 注意:这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中

举例

  • 代码:变量a在准备阶段会赋初始值,但不是1,而是0,在初始化阶段会被赋值为 1
1
2
3
4
5
6
7
public class HelloApp {
private static int a = 1; //prepare:a = 0 ---> initial : a = 1

public static void main(String[] args) {
System.out.println(a);
}
}

解析(Resolve)

解析

  1. 将常量池内的符号引用转换为直接引用的过程
  2. 事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行
  3. 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java虚拟机规范》的class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
  4. 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT Class info、CONSTANT Fieldref info、CONSTANT Methodref info等

初始化阶段#

  1. 初始化阶段就是执行类构造器方法<clinit>()的过程
  2. 此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。也就是说,当我们代码中包含static变量的时候,就会有clinit方法
  3. <clinit>()方法中的指令按语句在源文件中出现的顺序执行
  4. <clinit>()不同于类的构造器。(关联:构造器是虚拟机视角下的<init>()
  5. 若该类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕
  6. 虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ClassInitTest {
private static int num = 1;
private static int number = 10; //linking之prepare: number = 0 --> initial: 10 --> 20

static {
num = 2;
number = 20;
System.out.println(num);
//System.out.println(number); //报错:非法的前向引用(可以赋值,但不能调用)
}

public static void main(String[] args) {
System.out.println(ClassInitTest.num);//2
System.out.println(ClassInitTest.number);//10
}
}

静态变量 number 的值变化过程如下

  • 准备阶段时:0
  • 执行静态变量初始化:10
  • 执行静态代码块:20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
blic class ClassInitTest {
private static int num = 1;

static{
num = 2;
number = 20;
System.out.println(num);
//System.out.println(number); //报错:非法的前向引用(可以赋值,但不能调用)
}

private static int number = 10; //linking之prepare: number = 0 --> initial: 20 --> 10

public static void main(String[] args) {
System.out.println(ClassInitTest.num); //2
System.out.println(ClassInitTest.number); //10
}
}

静态变量 number 的值变化过程如下

  • 准备阶段时:0
  • 执行静态代码块:20
  • 执行静态变量初始化:10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ClinitTest {
//任何一个类声明以后,内部至少存在一个类的构造器
private int a = 1;
private static int c = 3;

public static void main(String[] args) {
int b = 2;
}

public ClinitTest(){
a = 10;
int d = 20;
}

}
  • 在构造器中:
    • 先将类变量 a 赋值为 10
    • 再将局部变量赋值为 20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ClinitTest1 {
static class Father{
public static int A = 1;
static{
A = 2;
}
}

static class Son extends Father{
public static int B = A;
}

public static void main(String[] args) {
//加载Father类,其次加载Son类。
System.out.println(Son.B);//2
}
}

如上代码,加载流程如下:

  • 首先,执行 main() 方法需要加载 ClinitTest1 类
  • 获取 Son.B 静态变量,需要加载 Son 类
  • Son 类的父类是 Father 类,所以需要先执行 Father 类的加载,再执行 Son 类的加载
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
public class DeadThreadTest {
public static void main(String[] args) {
Runnable r = () -> {
System.out.println(Thread.currentThread().getName() + "开始");
DeadThread dead = new DeadThread();
System.out.println(Thread.currentThread().getName() + "结束");
};

Thread t1 = new Thread(r, "线程1");
Thread t2 = new Thread(r, "线程2");

t1.start();
t2.start();
}
}

class DeadThread {
static {
if (true) {
System.out.println(Thread.currentThread().getName() + "初始化当前类");
while (true) {

}
}
}
}

程序卡死,分析原因:

  • 两个线程同时去加载 DeadThread 类,而 DeadThread 类中静态代码块中有一处死循环
  • 先加载 DeadThread 类的线程抢到了同步锁,然后在类的静态代码块中执行死循环,而另一个线程在等待同步锁的释放
  • 所以无论哪个线程先执行 DeadThread 类的加载,另外一个类也不会继续执行

类加载器的分类#

类加载器概述#

类加载器的分类

  1. JVM支持两种类型的类加载器 。分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
  2. 从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
  3. 无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个,如下所示
  4. 这里的四者之间是包含关系,不是上层和下层,也不是子父类的继承关系。

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MDk0MTQ5MjIzLnBuZw

ExtClassLoader 继承树

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTcwODM3NTM4LnBuZw

  • AppClassLoader 继承树aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTcwOTE4MDM3LnBuZw

代码

  • 我们尝试获取引导类加载器,获取到的值为 null ,这并不代表引导类加载器不存在,因为引导类加载器右 C/C++ 语言,我们获取不到
  • 两次获取系统类加载器的值都相同:sun.misc.Launcher$AppClassLoader@18b4aac2 ,这说明系统类加载器是全局唯一的
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
public class ClassLoaderTest {
public static void main(String[] args) {

//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

//获取其上层:扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d

//获取其上层:获取不到引导类加载器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null

//对于用户自定义类来说:默认使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

//String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null

}
}

虚拟机自带的加载器#

启动类加载器#

启动类加载器(引导类加载器,Bootstrap ClassLoader)

  1. 这个类加载使用C/C++语言实现的,嵌套在JVM内部
  2. 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类
  3. 并不继承自java.lang.ClassLoader,没有父加载器
  4. 加载扩展类和应用程序类加载器,并作为他们的父类加载器(当他俩的爹)
  5. 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类

扩展类加载器#

扩展类加载器(Extension ClassLoader)

  1. Java语言编写,由sun.misc.Launcher$ExtClassLoader实现
  2. 派生于ClassLoader类
  3. 父类加载器为启动类加载器
  4. 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载

4.2.3、系统类加载器#

应用程序类加载器(系统类加载器,AppClassLoader)

  1. Java语言编写,由sun.misc.LaunchersAppClassLoader实现
  2. 派生于ClassLoader类
  3. 父类加载器为扩展类加载器
  4. 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
  5. 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
  6. 通过classLoader.getSystemclassLoader()方法可以获取到该类加载器
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
public class ClassLoaderTest1 {
public static void main(String[] args) {

System.out.println("**********启动类加载器**************");
//获取BootstrapClassLoader能够加载的api的路径
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL element : urLs) {
System.out.println(element.toExternalForm());
}
//从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader);//null

System.out.println("***********扩展类加载器*************");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}

//从上面的路径中随意选择一个类,来看看他的类加载器是什么:扩展类加载器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
**********启动类加载器**************
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/jfr.jar
file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/classes
null
***********扩展类加载器*************
C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext
C:\WINDOWS\Sun\Java\lib\ext
sun.misc.Launcher$ExtClassLoader@7ea987ac

用户自定义类加载器#

为什么需要自定义类加载器?

在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定制类的加载方式。那为什么还需要自定义类加载器?

  1. 隔离加载类
  2. 修改类加载的方式
  3. 扩展加载源
  4. 防止源码泄漏

如何自定义类加载器?

  1. 开发人员可以通过继承抽象类java.lang.ClassLoader类的方式,实现自己的类加载器,以满足一些特殊的需求
  2. 在JDK1.2之前,在自定义类加载器时,总会去继承ClassLoader类并重写loadClass()方法,从而实现自定义的类加载类,但是在JDK1.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findclass()方法中
  3. 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URIClassLoader类,这样就可以避免自己去编写findclass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。
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 class CustomClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {

try {
byte[] result = getClassFromCustomPath(name);
if (result == null) {
throw new FileNotFoundException();
} else {
return defineClass(name, result, 0, result.length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}

throw new ClassNotFoundException(name);
}

private byte[] getClassFromCustomPath(String name) {
//从自定义路径中加载指定类:细节略
//如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作。
return null;
}

public static void main(String[] args) {
CustomClassLoader customClassLoader = new CustomClassLoader();
try {
Class<?> clazz = Class.forName("One", true, customClassLoader);
Object obj = clazz.newInstance();
System.out.println(obj.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}

关于 ClassLoader#

ClassLoader 类介绍

  • ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MTAzNTE2MTM4LnBuZw

  • sun.misc.Launcher 它是一个java虚拟机的入口应用

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MTAzNjM2MDAzLnBuZw

获取class的途径

aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzI3MTk0MTM1MTk4LnBuZw

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
public class ClassLoaderTest2 {
public static void main(String[] args) {
try {

//1.Class.forName().getClassLoader()
ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader); // String 类由启动类加载器加载,我们无法获取

//2.Thread.currentThread().getContextClassLoader()
ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
System.out.println(classLoader1);

//3.ClassLoader.getSystemClassLoader().getParent()
ClassLoader classLoader2 = ClassLoader.getSystemClassLoader();
System.out.println(classLoader2);

} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
// 输出
null
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2

双亲委派机制#

双亲委派机制原理#

双亲委派机制的原理

Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;

  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;

  3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

  4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MTA1MTUxMjU4LnBuZw

Example1

  • 我们自己建立一个 java.lang.String 类,写上 static 代码块
1
2
3
4
5
6
7
package java.lang;

public class String {
static{
System.out.println("我是自定义的String类的静态代码块");
}
}
  • 在另外的程序中加载 String 类,看看加载的 String 类是 JDK 自带的 String 类,还是我们自己编写的 String 类
1
2
3
4
5
6
7
8
9
10
public class StringTest {

public static void main(String[] args) {
java.lang.String str = new java.lang.String();
System.out.println("hello,atguigu.com");

StringTest test = new StringTest();
System.out.println(test.getClass().getClassLoader());
}
}
  • 程序并没有输出我们静态代码块中的内容,可见仍然加载的是 JDK 自带的 String 类

Example2

1
2
3
4
5
6
7
8
9
10
11
package java.lang;

public class String {
static{
System.out.println("我是自定义的String类的静态代码块");
}
//错误: 在类 java.lang.String 中找不到 main 方法
public static void main(String[] args) {
System.out.println("hello,String");
}
}
  • 由于双亲委派机制找到的是 JDK 自带的 String 类,在那个 String 类中并没有 main() 方法

当我们加载jdbc.jar 用于实现数据库连接的时候

  1. 首先我们需要知道的是 jdbc.jar是基于SPI接口进行实现的

  2. 所以在加载的时候,会进行双亲委派,最终从根加载器中加载 SPI核心类,然后再加载SPI接口类

  3. 接着在进行反向委托,通过线程上下文类加载器进行实现类 jdbc.jar的加载。

    aHR0cDovL2hleWdvLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vaW1hZ2VzL2ltYWdlLTIwMjAwNzA1MTA1ODEwMTA3LnBuZw

双亲委派机制优势#

双亲委派机制的优势

通过上面的例子,我们可以知道,双亲机制可以

  1. 避免类的重复加载
  2. 保护程序安全,防止核心API被随意篡改
    1. 自定义类:java.lang.String 没有调用
    2. 自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)

Example:在 java.lang 包下整个 ShkStart 类

1
2
3
4
5
6
7
package java.lang;

public class ShkStart {
public static void main(String[] args) {
System.out.println("hello!");
}
}

出于保护机制,java.lang 包下不允许我们自定义类

沙箱安全机制#

自定义String类时:在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java.lang.String.class),报错信息说没有main方法,就是因为加载的是rt.jar包中的String类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制。

其他#

如何判断两个class对象是否相同?

在JVM中表示两个class对象是否为同一个类存在两个必要条件:

  1. 类的完整类名必须一致,包括包名
  2. 加载这个类的ClassLoader(指ClassLoader实例对象)必须相同
  3. 换句话说,在JVM中,即使这两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的

对类加载器的引用

  1. JVM必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的
  2. 如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中
  3. 当解析一个类型到另一个类型的引用的时候,JVM需要保证这两个类型的类加载器是相同的

类的主动使用和被动使用

Java程序对类的使用方式分为:主动使用和被动使用。主动使用,又分为七种情况:

  1. 创建类的实例
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(比如:Class.forName(“com.atguigu.Test”))
  5. 初始化一个类的子类
  6. Java虚拟机启动时被标明为启动类的类
  7. JDK7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化

除了以上七种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化,即不会执行初始化阶段(不会调用 clinit() 方法和 init() 方法)

LeetCode Daily Notes#

6/3/2020#

  • string.trim() is used to trim leading and tailing spaces in a String

  • If we want to split a string by multiple spaces we can use string.split(“\\s+”)

  • If we want to concatenate a list of string, we can use String.join(“ “, wordList).

  • Lambda expression used in sorting List or Array

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    arr1.sort((a, b) -> Integer.compare(a[1] - a[0], b[1] - b[0]));
    // or
    arr1.sort(Comparator.comparingInt(a -> a[1] - a[0]));
    // or this method is more flexible
    arr2.sort((a, b) -> {
    if(a[0] > a[1]) {
    return 1;
    } else if (a[0] < a[1]) {
    return -1;
    } else {
    return 0;
    }
    });

Spring 注解驱动开发#

容器(Container)#

配置类 == 配置文件

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
//配置类
@Configuration //告诉spring这事一个配置类
//@ComponentScan //value表示要扫描包的位置
//excludeFilters 要排除哪些Component,里面装着是Filter[]
@ComponentScan(value="com.atguigu", excludeFilters = {
@Filter(type="FilterType.ANNOTATION",classes={Controller.class})
})
//Filter的type是表明按照什么方式去排除,里面有5种,分别是:FilterType.ANNOTATION, FilterType.ASPECTJ, FilterType.ASSIGNABLE, FilterType.CUSTOM, FilterType.REGEX 在后面跟classes 表明要去掉哪些包
//FilterType.ANNOTATION 按照注解
//FilterType.ASSIGNABLE_TYPE 按照给定的类型
@Filter(type="FilterType.ASSIGNABLE_TYPE",classes={BookService.class})
//FilterType.ASPECTJ 使用ASPECTJ表达式
//FilterType.REGEX 使用正则表达式规则
//FilterType.CUSTOM 给定一个TypeFilter的实现类
@Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class}) //就是看MyTypeFilter里面的math返回true或者false来决定匹不匹配

//includeFilters 扫描的时候只需要包含哪些组件,里面装着是Filter[] 注意只包含生效的条件是要禁用默认规则,默认规则是扫描所有的因此要禁用掉
@ComponentScan(value="com.atguigu", includeFilters = {
@Filter(type="FilterType.ANNOTATION",classes={Controller.class})},useDefaultFilters = false)
//制定扫描策略,可以写多条
@ComponentScans(
value = {
@ComponentScan(value="com.atguigu", includeFilters = {
@Filter(type="FilterType.ANNOTATION",classes={Controller.class})},useDefaultFilters = false)
}
)
public class MainConfig{
//给容器中注册一个Bean; 类型为返回值的类型,id默认是方法名作为id
@Bean("person")
public Person person(){
return new Person("lisi",20);
}
}

MyTypeFilter.java 自定义Filter的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyTypeFilter implements TypeFilter{
//metadatareader读取的是当前正在扫描的类的信息
//MetaDataReaderFactory 可以获取到其他任何类的信息
@Override
public boolean math(MetadataReader metadataReader, MetaDataReaderFactory metadatareaderfactory) {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源信息(类的路径)
Resource resource = metadataReader.getResource();
//classMetadata 可以获取到当前类名 当前类子类、父类类名
return false;
}
}

使用@Scope调整作用域

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
@Configuration
//类中组件统一设置 满足当前条件,这个类中配置的所有bean注册才能生效
@Conditional({WindowsCondition.class})
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar})
public class MainConfig2{
/*
* 默认是单实例的
* ConfigurableBeanFactory#SCOPE_PROTOTYPE -> "prototype"
* ConfigurableBeanFacotry#SCOPE_SINGLETON -> "singleton"
* org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST -> request
* org.springframework.web.context.WebApplicationContext#SCOPE_SESSION -> session
*
* prototype: 多实例: ioc容器启动并不会去调用方法创建对象放在容器中 每次获取的时候才会调用方法创建对象
* singleton: 单实例(默认值) ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器中(map.get())拿。
* request: 同一请求创建一个实例
* session: 同一个session创建一个实例
*
* 懒加载 @Lazy
* 单实例bean:默认在容器启动的时候创建对象
* 懒加载: 容器启动不创建对象。在第一次使用(获取)Bean创建对象,并初始化。
*/
@Scope("prototype"/"singleton")
@Lazy
@Bean("person")
public Person person() {
return new Person("zhangsan",25);
}

/**
* @Conditional({Conditon}): 按照一定的条件进行判断,满足条件给容器中注册bean
* 如果系统是windows,给容器中注册("bill")
* 如果系统是linux,给容器中注册("linux")
*/
@Conditional({WindowsCondition.class})
@Bean("bill")
public Person person01(){
return new Person("Bill Gates",62);
}

@Conditional({LinuxCondition.class})
@Bean("linus")
public Person person02(){
return new Person("linus", 48);
}

/*
* 给容器中注册组件
* 1. 包扫描+标注注解(@Controller/@Service/@Service/@Repository/@Component)[自己写的]
* 2. 使用@Bean[导入的第三方包的组件] --> 每次导入都需要写一条 有些麻烦
* 3. @Import[快速的给容器导入一个组件]
* 1. @Import(要导入的组件) //容器中就会自动注册这个组件,id默认为全类名
* 2. @ImportSelector(返回需要导入的组件的全类名数组)
* 3. @ImportBeanDefinitionRegistrar: 手动注册bean到容器中
* 4. 使用Spring提供的FactoryBean(工厂bean)
* 1. 默认获取到的是工厂bean本身调用getObject创建的对象
* 2. 要获取工厂bean本身,我们需要给id前面加一个&
* &colorFactoryBean
*/

//工厂bean获取的是调用getObject方法创建的对象,直接打印显示的是ColorFactoryBean 但是调用getClass的时候返回的是Color
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}

}

Color

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//随便创一个,假装外部组件
public class Color{

}
public class Red{

}
public class Blue{

}
public class Yellow{

}
public class RainBow{

}

MyImportSelector.java

1
2
3
4
5
6
7
8
9
10
11
12
13
//自定义逻辑 返回需要导入的组件
public class MyImportSelector implements ImportSelector{

//返回值就是导入到容器中的组件全类名
//AnnotationMetadata 当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata){

//方法不要返回null
//return null;
return new String[]{"com.bean.Blue", "com.bean.Yellow"};
}
}

LinuxCondition.java

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
//判断是否是linux系统
public class LinuxCondition implements Condition {

/*
* ConditionContext: 判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata: 注释信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 是否是linux系统
//1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getregistry();

//可以判断容器中的bean注册情况,也可以给容器中注册bean
boolean definition = registry.containsBeanDefinition("person");

String property = environment.getProperty("os.name");
if(property.contains("Linux")) {
return true;
}
return false;
}
}

MyImportBeanDefinitionRegistrar.java –> 手工注册

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

/*
*
* AnnotationMetadata: 当前类的注解信息
* BeanDefinitionRegistry: BeanDefinition注册类
* 把所有需要添加到容器中的bean, 调用 beanDefinitionRegistry.registerBeanDefinitions 手工注册
*/

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("Red->要写全类名");
boolean definition2 = registry.containsBeanDefinition("Blue->要写全类名");
if(definition && definition2) {
//制定bean的定义信息 Bean的类型 Bean的scope
RootBeanDefinition beanDefinition new RootBeanDefinition(Rainbow.class);
//注册一个Bean
registry.registerBeanDefinitions("rainBow",beanDefinition);
}
}
}

ColorFactoryBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//创建一个spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color>{
//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() thorws Exception{
return new Color();
}

@Override
public Class<?> getObjectType() {
return Color.class
}

//是单例?
//true 这个bean是单实例,在容器中会保存一份
//false 这是一个多实例,每次获取都会创建一个新的bean -> 调用getObject方法
@Override
public boolean isSingleton(){
return false;
}
}

WindowsCondition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//判断是否是windows系统
public class WindowsCondition implements Condition {

/*
* ConditionContext: 判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata: 类的注解信息
*/

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")) {
return true;
}
return false;
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--配置文件-->

<!-- 包扫描、只要标注了@Controller, @Service, @Repository, @Component -->
<!-- use-default-filters true表示默认允许所有 -->
<context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan>
<context:property-placeholder location="classpath:person.properties"/>
<!-- init-method 和 destroy-method可以指定bean的初始化方法和销毁方法 -->
<bean id="person" class="com.bean.Person" scope="singleton" init-method="" destroy-method="">
<property name="age" value="18"></property>
<property name="name" value="zhangsan"></property>
</bean>
<!-- 开启基于注解版的切面功能 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

BookController.java

1
2
3
4
5
@Controller
public class BookController{
@AutoWired(required=false) // true:强制要求 false如果找不到就不装配了
private BookService bookService;
}

BookService.java

1
2
3
4
5
@Service
public class BookService{
@AutoWired
private BookDao bookDao;
}

BookDao.java

1
2
3
4
@Repository
public class BookDao{

}

Main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainTest{
public static void main(String... args) {
//配置文件方式启动
//返回ioc容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person bean = (Person) applicationContext.getBean("person");
System.out.println(bean);
//配置类方式启动
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean("person");
System.out.println(bean);

String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
//person
for(String string : namesForType) {
System.out.println(string);
}
}
}

IOCTest.java

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
public class IOCTest{
@Test
public void test01(){
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfig.class);
String[] definitionNames applicationContext.getBeanDefinitionName();
for(String name : definitionNames) {
System.out.println(name);
}
/* Output
* mainConfig
* bookController -> 要被排除掉
* bookDao
* bookService -> 要被排除掉
* person
*/
}

public void test02(){
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfig2.class);
String[] definitionNames applicationContext.getBeanDefinitionName();
for(String name : definitionNames) {
System.out.println(name);
}
//默认是单实例的
//bean == bean2
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
}

public void test03(){
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfig2.class);
//获取当前context运行环境
ConfigurableEnvironment environment = applicationContext.getEnvironment();
environment.getProperty("os.name");
//动态获取环境变量的值
System.out.println(property);

String[] definitionNames applicationContext.getBeanNamesForType(Person.class);
for(String name : definitionNames) {
System.out.println(name);
}
Map<String, Person> persons = applicationContext.getBeansOfType(Person.class);
System.out.println(persons);
}

@Test
public void testImport(){
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfig2.class);
String[] definitionNames applicationContext.getBeanDefinitionName();
for(String name : definitionNames) {
System.out.println(name);
}
Blue bean = applicationContext.getBean(Blue.class);
System.out.println(bean);

//工厂bean获取的是调用getObject方法创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的类型" + bean2.getClass());
//Output: color

//通过id获取
Object bean3 = applicationContext.getBean("&colorFactoryBean");
System.out.println("bean的类型" + bean3.getClass());
//class -> ColorFactoryBean
}
}

Bean的生命周期

MainConfigOfLifeCycle.java

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
/* bean的生命周期
* bean的创建---初始化---销毁的过程
* 容器管理bean的生命周期
* 我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
*
* 构造(对象创建)
* 单实例,在容器启动的时候创建对象
* 多实例,在容器获取的时候创建对象
* 初始化
* 对象创建完成,并赋值好,调用初始方法。。。
* 销毁
* 单实例bean 容器关闭的时候进行销毁
* 多实例bean 容器不会管理这个bean 容器不会调用销毁方法
*
* 遍历得到容器中所有的BeanPostProcessor 挨个执行beforeInitialization 一旦返回null 就直接跳出for循环 不会执行后面的BeanPostProcessor.applyBeanPostProcessorsBeforeInitialization
*
*
* BeanPostProcessor的原理
* polulateBean(beanName, mbd, instanceWrapper); //给bean进行属性赋值
* => initialization
* {
* applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
* invokeInitMethods(beanName, wrappedBean, mbd); //执行自定义初始化方法
* applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
* }
*
* 1 指定初始化和销毁方法
* init-method 和 destroy-method 以前使用配置文件指定
* 通过@Bean指定init-method和destroy-method
*
* 2 通过让bean实现Initializingbean来定义初始化逻辑 DisposableBean(定义销毁逻辑)
*
* 3 可以使用JRS250
* @PostConstruct 在bean创建完成并且属性赋值完成 来执行初始化
* @PreDestroy 在容器销毁bean之前通知我们进行清理工作
*
* 4 BeanPostProcessor[interface] bean的后置处理器
* 在bean初始化前后做一些处理工作->即使bean没有指定初始化方法
* postProcessBeforeInitialization: 在初始化之前进行后置处理工作
* postProcessAfterInitilization: 在初始化之后进行后置处理工作
*
* Spring底层对BeanPostProcessor的使用 ->底层好多
* bean的赋值 注入其他组件 @Autowired生命周期注解功能 @Async BeanPostProcessor
*
*/
@Configuration
@ComponentScan("com.bean")
public class MainConfigOfLifeCycle{

@bean(initMethod="init",destroyMethod="destroy")
public Car car(){
return new Car();
}
}

MyBeanPostProcessor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* 自定义后置处理器,初始化前后进行处理工作
* 将后置处理器加入到容器中
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeanException{
System.out.println(beanName + "=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitilization(Object bean,String beanName) throws BeanException{
System.out.println(beanName + "=>" + bean);
return bean;
}
}

Car.java

1
2
3
4
5
6
7
8
9
10
11
public class Car{
public Car(){
System.out.println("car constructor");
}
public init(){
System.out.println("car init...");
}
public destroy(){
System.out.println("car destroy...");
}
}

Cat.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class Cat implements InitilizingBean, DisposableBean{
public Cat(){
System.out.println("cat instructor");
}

@Override
public void destroy() throws Exception{
System.out.println("cat... destroy...");
}
@Override
public void afterPropertiesSet() throws Exception{
System.out.println("Cat... afterPropertiesSet...");
}
}

Dog.java

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
@Component
public class Dog implements ApplicationContextAware{

private ApplicationContext applicationContext;

public Dog(){
System.out.println("dog constructor");
}

//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("dog @PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void destroy(){
System.out.println("dog @PostDestroy");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeanException{
this.applicationContext = applicationContext;
}
}

IOCTest_LifeCycle.java

1
2
3
4
5
6
7
8
9
10
11
public class IOCTest_LifeCycle{

@Test
public void test01(){
//创建ioc容器
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");

applicationContext.close();
}
}

属性赋值

MainConfigOfPropertyValues.java

1
2
3
4
5
6
7
8
9
@Configuration
//使用@PropertySource读取外部配置文件中的属性 保存到运行的环境变量中 加载完外部的配置文件以后再使用${}取出配置文件的值
@PropertySource(value={"classpath:/person.properties"})
public class MainConfigOfPropertyValues{
@Bean
public Person person(){

}
}

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Person{
//使用@Value赋值
//基本数值
//可以写SpEL #{}
//可以写${} 取出配置文件中的值[properties](在运行环境变量里面的值)

@Value("zhangsan")
private String name;

@Value("#{20-2}")
private Integer age;
@Value("${person.nickName}")
private String NickName;
}

person.properties

1
person.nickName = xxx

IOCTest_LifeCycle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class IOCTest_PropertyValue{

@Test
public void test01(){
//创建ioc容器
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfigOfPropertyValues.class);
System.out.println("容器创建完成");
Person person = (Person)applicationContext.getBean("person");
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
String[] definitionNames applicationContext.getBeanDefinitionName();
for(String name : definitionNames) {
System.out.println(name);
}
applicationContext.close();
}
}

自动装配

MainConfigAutowired.java

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
/*
* 自动装配
* Spring利用依赖注入(DI) 完成对IOC容器中各个组件的关系依赖赋值
* 1. AutoWired 自动注入
* 1. 默认优先按照类型去容器中找对应的组件,找到就赋值
* 2. 如果找到相同类型的组件,再将属性的名称作为组件的id去容器中查找
* applicationContext.getBean("bookDao")
* 3. @Qualifier("bookdao")使用@Qualifier指定需要装配的组件的id
* 4. 自动装配默认一定要将属性赋值好,没有就报错
* 5. Primary 让Spring进行自动装配的时候默认使用首选bean 这个时候不用Qualifier 但是也可以继续使用@Qualifier指定需要装配的bean的名字
* 2. Spring还支持使用@Resource(JSR250)@Inject(JSR330)[Java规范的注解]
* @Resource:
* 可以和Autowired一样实现自动装配功能,但是这个注解默认是按照组件名称进行装配 也可以使用@Resource(name="bookDao2")指定装配的bean
* 没有支持@Primary功能没有支持@Autowired(required=false)
* @Inject:
* 需要导入javax.inject的包,和Autowired功能一样
* @Autowired:Spring定义的:@Resource,@Inject都是java规范
* 3. @Autowired: 构造器 参数 方法 属性 都是从容器中获取参数组件的值
* 1. 标在方法位置 @Bean标注的方法,创建对象的时候 方法参数的值从容器中获取 默认不写也是一样的 都能自动装配
* 2. 标在构造器上 如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是能够自动从容器中获取
* 3. 放在参数位置上
* 4. 自定义组件想要用Spring容器底层的一些组件(ApplicationContext, BeanFactory)
* 自定义组件实现xxxAware 在创建对象的时候 会调用接口规定的方法注入相关组件 Aware
* 把Spring底层一些组件注入到自定义的bean中
* xxxAware 功能 使用xxxProcessor来处理
*/
@Configuration
@ComponentScan({"com.service","com.dao","com.controller"})
public class MainConfigAutowired{


}

Boss.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//默认加载ioc容器中的组件 容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Boss{
//@Autowired
private Car car;

@Autowired
public Boss(@Autowired Car car){
this.car = car;
}

//@Autowired //标注在方法上 Spring容器创建当前对象 就会调用方法 完成赋值
//方法使用的参数,自定义类型的值从ioc容器中获取
public void setCar(Car car){
this.car = car;
}
}

IOCTest_Autowired.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class IOCTest_Autowired{

@Test
public void test01(){
//创建ioc容器
AnnotationConfigapplicationContext applicationContext new AnnotationConfigapplicationContext(MainConfigAutowired.class);
System.out.println("容器创建完成");
BookService bookService = applicationContext.getBean(BookService.class);
System.out.println(bookService);
BookDao bookDao = applicationContext.getBean(BookDao.class);
//bookDao == bookService.bookDao;
applicationContext.close();
}
}

Profile

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
/*
* Profile
* spring 为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能
* 开发环境,测试环境,生产环境
* 数据源,(/A)(/B)(/C)
* @Profile 指定组件在哪个环境的情况下才能被注册到容器中,不指定 任何环境下都能注册这个组件
* 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器 默认是default 环境
* 运行方法 添加运行参数 -Dspring.profiles.active=test
* 代码方法 使用无参构造器
* 1. 创建一个applicationContext
* 2. 设置需要激活的环境
* applicationContext.getEnvironment().setActiveProfiles("test","dev");
* 3. 注册主配置类
* applicationContext.register(MainConfigOfProfile.class);
* 4. 启动刷新容器
* applicationContext.refresh();
* 写在配置类上 只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
* 没有标注环境标识的bean在,任何环境下都是加载的
*/
@Configuration
@PropertySource("classpath:/dbconfig.proerties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware{


@Value("db.user")
private String user;

private StringValueResolver valueResolver;

@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value(db.password) String pwd) {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrc(valueResolverresolveStringValue("${db.driveClass}"));
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return null;
}

@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setJdbcUrc("jdbc:mysql://localhost:3306/");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return null;
}

@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setJdbcUrc("jdbc:mysql://localhost:3306/");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return null;
}

@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
}

}

dbconfig.proerties

1
2
3
db.user = root
db.password = 123456
db.driverClass = com.mysql.jdbc.Driver

MainConfigOfAOP.java

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
/*
* AOP [动态代理]
* 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的变成方式
* 1.导入AOP模块 springaop
* 2.定义一个业务逻辑类(MathCalculator) 在业务逻辑运行的时候将日志进行打印,在运行方法前 运行方法结束 方法出现异常的时候 -> 解耦合
* 3.定义一个日志切面类(LogAspects)切面类里面的方法需要动态感知MathCalculator运行到哪里然后执行
* 通知方法:
* 前置通知(@Before) -> logStart 在目标方法div运行之前运行
* 后置通知(@After) -> logEnd 在目标方法div运行结束之后运行 无论方法正常结束 异常结束 都调用
* 返回通知(@AfterReturning) -> logReturn 在目标方法div正常返回之后运行
* 异常通知(@AfterThrowing) -> logException 在目标方法运行出现异常以后运行
* 环绕通知(@Around) -> 动态代理, 手动推进目标方法运行(joinPoint.procced)
* 4.给切面类的目标方法标注何时何地运行 通知注解
* 5.将切面类和业务逻辑了(目标方法所在类)都加入到容器中
* 6.必须告诉spring哪个类是切面类(给切面类上加一个注解@Aspect)
* [7]给配置类中加入@EnableAspectJAutoProxy 开启基于注解的AOP模式
* 在spring中有很多的@Enablexxx 用于开启某些功能
* 三步
* 1) 将业务逻辑组件和切面类都加入到容器中 告诉spring哪个是切面类(@Aspect)
* 2) 在切面类上的每一个通知方法上标注通知注解 告诉spring何时何地运行(切入点表达式)
* 3) 开启基于注解的aop模式 @EnableAspectJAutoProxy
*
* AOP原理 [看给容器中注册了什么组件 这个组件什么时候工作 这个组件工作时候的功能是什么]
* @EnableAspectJAutoProxy;
* 1. @EnableAspectJAutoProxy是什么
* @Import(AspectJAutoProxyRegistar.class) 给容器中导入AspectJAutoProxyRegistar
* 利用AspectJAutoProxyRegistar自定义给容器中注册bean
* internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
* 给容器中注册一个叫AnnotationAwareAspectJAutoProxyCreator
* 2. AnnotationAwareAspectJAutoProxyCreator
* -> AnnotationAwareAdvisorAspectJAutoProxyCreator
* -> AspectJAwareAdvisorAutoProxyCreator
* -> AbstractAdvisorAutoPorxyCreator
* -> AbstractAutoProxyCreator
* implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
* 关注后置处理器(在bean初始化完成前后做事情),自动装配BeanFactory
*
*
* AbstractAutoProxyCreator.setBeanFactory()
* AbstractAutoProxyCreator 有后置处理器的逻辑
*
* AbstractAdvisorAutoPorxyCreator.setBeanFactory() -> initBeanFactory()
* AnnotationAwareAdvisorAspectJAutoProxyCreator.initBeanFactory()
* 流程
* 1) 传入配置类 创建ioc容器
* 2) 注册配置类 调用refresh 刷新容器
* 3) registerBeanPostProcessors(beanFactory);注册bean的后置处理器
* 1) 先获取ioc容器中已经定义了的要创建对象的所有BeanPostProcessor
* 2) 给容器中加入别的BeanPostProcessor
* 3) 优先注册实现了PriorityOrdered接口的BeanPostProcessor
* 4) 再给容器中注册实现了Ordered接口的BeanPostProcessor
* 5) 注册没实现优先级接口的BeanPostProcessor
* 6) 注册BeanPostProcessor 实际上是创建BeanPostProcessor对象 保存在容器中
* 创建internalAutoProxyCreator的BeanPostProcessor[AnnotationAwareAspectJAutoProxyCreator]
* 1)创建Bean的实例
* 2)populateBean 给bean的各种属性赋值
* 3)initializeBean 初始化bean
* 1) invokeAwareMethods 处理Aware接口的方法回调
* 2) applyBeanPostProcessorBeforeInitilization() 应用后置处理器的BeforeInitilization方法
* 3) invokeInitMethod 执行自定义的初始化方法
* 4) applyBeanPostProcessorBeforeInitilization 执行后置处理器的AfterInitilization方法
* 4)BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功并且调用了他的 initBeanFactory方法 -> advisorbuilder
* 7) 把BeanPostProcessor注册到BeanFactory中
* beanFactory.addBeanPostProcessor(postProcessor)
*==============以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程===========
* AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
*
*
*/
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP{

//业务逻辑加入类中
@Bean
public MathCalculator calculator(){
return new MathCalculator();
}

//切面类加入到容器中
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}

MathCalculator

1
2
3
4
5
6
public class MathCalculator{

public int div(int i, intj) {
return i/j;
}
}

LogAspects.java

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
//告诉spring当前类是切面类
@Aspect
public class LogAspects{

//抽取公共的切入点表达式
//1. 本类引用 直接在后面写@Before("pointCut()")
//2. 其他的切面类引用 使用方法全名
@PointCut("execution(public int com.aop.MathCalculator.*(..))")
public void pointCut(){};

//@Before在目标方法之前切入,切入点表达式(指定在哪个方法切入 可以使用通配符* means 所有方法 ..指定所有函数参数)
@Before("public int com.aop.MathCalculator.*(..)")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(joinPoint.getSignator().getName()+"运行除法"+Arrays.asList(args));
}
@After("pointCut()")
public void logEnd(){
System.out.println("除法结束");
}

//JoinPoint 一定要出现在参数表的第一位 @AfterReturning(value="pointCut()",returning="result")
public void logReturn(JointPoint joinPoint, Object result){
System.out.println("除法正常返回"+result);
}
@AfterThrowing(value="pointCut()", throwing="exception")
public void logExpection(Exception exception){
System.out.println("除法异常信息")
}
}

This is a personal notes for Java spring framework

Spring introduction#

What is spring#

It is a very popular framework for building Java application and initially a simpler and light weight alternative to J2EE. Also, it provides a large number of helper classes.

Update on Spring 5#

  • Upgraded minimum requirements for Java 8 or higher
  • Deprecated legacy integration for: Tiles, Velocity, portlet, Guava etc
  • Upgraded Spring MVC to use new versions of Servlet API 4.0
  • Added new reactive programming framework: Spring WebFlux

Spring Core Framework#

Goals of Spring#

  • Lightweight development with Java POJOs(Plain-Old-Java-Objects)
  • Dependency injection to promote loose coupling
  • Declarative programming with Aspect-Oriented-Programming(AOP)
  • Minimize boilerplate Java code

Core Container#

Factory for creating beans. Manage bean dependencies.

  • Beans
    • A “Spring Bean” is simply a Java object. When Java objects are created by the Spring Container, then Spring refers to them as “Spring Beans”. Spring Beans are created from normal Java classes …. just like Java objects.
  • Core
  • SpEl
    • Spring extression language
  • Contex

Infrastructure#

Aspect Oriented Programming. Add functionality to objects declaratively.

Logging, security,transactions,etc…

  • Aop
  • Aspects
  • Inplementation
    • Java agents to remotely monitor your app with JMX(Java Management Extension)
  • Messaging

Data Access Layer#

  • JDBC -> helper classes to make easier to access Database
  • ORM -> Object Relational Mapping -> JPA / Hibernate
  • Transactions
  • OXM
  • JMS (Java Message Service)
    • For sending async messages to a Message Broker
    • Spring provides helper classes for JMS

Web Layer#

All Web related classes. Home of the Spring MVC framework.

  • Servlet
  • WebSocket
  • Web
  • Portlet

Test Layer#

Supports Test-Driven-Development(TDD). Mock objects and out-of-container testing

  • Unit
  • Integration
  • Mock

Inversion of Control#

Spring Container#

  • Primary functions
    • Create and manage objects(Inversion of Control)
    • Intect object’s dependencies(Dependency Injection)
  • Configuring Spring Container
    • XML Configuration file
    • Java Annotations
    • Java Source Code
  • Spring container is generically known as Application Context
  • Specialized implementations
    • ClassPathXmlApplicationContext
    • AnnotationConfigApplicationContext
    • GenericWebApplicationContext
    • Others…

Dependency Injection#

The dependency inversion principle.

The client delegates to calls to another object

the responsibility of providign its dependencies

Injection Types#

  • There are many types of injection with Spring
  • We will cover the two most common
    • Constructor Injection
    • Setter Injection
  • Autowiring –> discuss later

Basic grammer#

How to call async method in sync method#

suppose function A() is marked as async, if we want to call A in function B, usually we use await and marked b as async. However, if we dont want to modify the return type of B. To solve that, I decide to use anonymous function to encapsulate the calling of function A, then receive the return_val of A sync.

1
2
3
4
5
6
7
public int B(){
var asyncTask = Task.Run(async () => {
IEnumerable<returnObj>res = await A();
return res; });
asyncTask.Wait();
return asyncTask.Result().Count();
}

Utility#

ELMAH#

log4net#

#

Introduction#

AutoMapper is an object-object mapper. Object-object mapping works by transforming an input object of one type into an output object of a different type. What makes AutoMapper interesting is that it provides some interesting conventions to take the dirty work out of figuring out how to map type A to type B. As long as type B follows AutoMapper’s established convention, almost zero configuration is needed to map two types.

Usage#

Once you have your types you can create a map for the two types using a MapperConfiguration and CreateMap. You only need one MapperConfiguration instance typically per AppDomain and should be instantiated during startup. More examples of initial setup see in Setup.

1
var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());

The type on the left is the source type, and the type on the right is the destination type. To perform a mapping, call one of the Map overloads:

1
2
3
4
var mapper = config.CreateMapper();
// or
var mapper = new Mapper(config);
OrderDto dto = mapper.Map<OrderDto>(order);
1
2
3
4
var obj2 = Mapper.Map<Obj1, Obj2>(obj1);
//obj1 is type of Obj1
//obj2 is type of Obj2
// it will map the same Attribute with Obj1 and Obj2 and return a new obj which type of Obj2
0%