();
103 | addedColumn.add("Added");
104 | BoundStatement statement = preparedStatement.bind(addedColumn, "1");
105 | session.execute(statement);
106 |
107 |
108 | //通过cassandraOperations查询
109 | Select select = QueryBuilder.select().from("person");
110 | select.where(QueryBuilder.eq("id", "1"));
111 | Person person = cassandraOperations.selectOne(select, Person.class);
112 | System.out.println("==> Finally, cassandraOperations:");
113 | System.out.println(person.toString());
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/db/cassandra-demo/src/main/resources/application.xml:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------
/db/cassandra-demo/src/main/resources/cassandra.properties:
--------------------------------------------------------------------------------
1 | #cassandra数据库连接
2 | #节点ip
3 | cassandra_contactpoints=127.0.0.1
4 | #端口
5 | cassandra_port=9042
6 | #当前操作键空间
7 | cassandra_keyspace=mstest
8 | #登录用户名
9 | cassandra_username=cassandra
10 | #登录密码
11 | cassandra_password=cassandra
12 |
--------------------------------------------------------------------------------
/db/consistent-hash/.gitignore:
--------------------------------------------------------------------------------
1 | # maven ignore
2 | target/
3 | *.jar
4 | *.war
5 | *.zip
6 | *.tar
7 |
8 | # eclipse ignore
9 | .settings/
10 | .project
11 | .classpath
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | logs/
21 | *.doc
22 | *.log
23 | *.cache
24 | *.diff
25 | *.patch
26 | *.tmp
27 |
28 | # system ignore
29 | .DS_Store
30 | Thumbs.db
31 |
--------------------------------------------------------------------------------
/db/consistent-hash/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | io.github.flylib
8 | consistent-hash
9 | jar
10 | 1.0.0
11 | https://github.com/flylib
12 |
13 |
14 |
15 | org.apache.maven.plugins
16 | maven-compiler-plugin
17 | 3.7.0
18 |
19 | 1.8
20 | 1.8
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/db/consistent-hash/src/main/java/io/github/flylib/consistenthash/ByteUtils.java:
--------------------------------------------------------------------------------
1 | package io.github.flylib.consistenthash;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.nio.ByteBuffer;
5 | import java.nio.charset.Charset;
6 |
7 |
8 | /**
9 | * @author liushaoming
10 | *
11 | * Utilities for byte process
12 | */
13 | public final class ByteUtils {
14 | public static final String DEFAULT_CHARSET_NAME = "utf-8";
15 | public static final Charset DEFAULT_CHARSET = Charset
16 | .forName(DEFAULT_CHARSET_NAME);
17 | /**
18 | * if it is testing,check key argument even if use binary protocol. The user
19 | * must never change this value at all.
20 | */
21 | public static boolean testing;
22 |
23 | private ByteUtils() {
24 | }
25 |
26 | public static boolean isNumber(String string) {
27 | if (string == null || string.isEmpty()) {
28 | return false;
29 | }
30 | int i = 0;
31 | if (string.charAt(0) == '-') {
32 | if (string.length() > 1) {
33 | i++;
34 | } else {
35 | return false;
36 | }
37 | }
38 | for (; i < string.length(); i++) {
39 | if (!Character.isDigit(string.charAt(i))) {
40 | return false;
41 | }
42 | }
43 | return true;
44 | }
45 |
46 | public static final byte[] getBytes(String k) {
47 | if (k == null || k.length() == 0) {
48 | throw new IllegalArgumentException("Key must not be blank");
49 | }
50 | try {
51 | return k.getBytes(DEFAULT_CHARSET_NAME);
52 | } catch (UnsupportedEncodingException e) {
53 | throw new RuntimeException(e);
54 | }
55 | }
56 |
57 |
58 | private static int maxKeyLength = 250;
59 |
60 |
61 | public static final int normalizeCapacity(int requestedCapacity) {
62 | switch (requestedCapacity) {
63 | case 0:
64 | case 1 << 0:
65 | case 1 << 1:
66 | case 1 << 2:
67 | case 1 << 3:
68 | case 1 << 4:
69 | case 1 << 5:
70 | case 1 << 6:
71 | case 1 << 7:
72 | case 1 << 8:
73 | case 1 << 9:
74 | case 1 << 10:
75 | case 1 << 11:
76 | case 1 << 12:
77 | case 1 << 13:
78 | case 1 << 14:
79 | case 1 << 15:
80 | case 1 << 16:
81 | case 1 << 17:
82 | case 1 << 18:
83 | case 1 << 19:
84 | case 1 << 21:
85 | case 1 << 22:
86 | case 1 << 23:
87 | case 1 << 24:
88 | case 1 << 25:
89 | case 1 << 26:
90 | case 1 << 27:
91 | case 1 << 28:
92 | case 1 << 29:
93 | case 1 << 30:
94 | case Integer.MAX_VALUE:
95 | return requestedCapacity;
96 | }
97 |
98 | int newCapacity = 1;
99 | while (newCapacity < requestedCapacity) {
100 | newCapacity <<= 1;
101 | if (newCapacity < 0) {
102 | return Integer.MAX_VALUE;
103 | }
104 | }
105 | return newCapacity;
106 | }
107 |
108 | public static final boolean stepBuffer(ByteBuffer buffer, int remaining) {
109 | if (buffer.remaining() >= remaining) {
110 | buffer.position(buffer.position() + remaining);
111 | return true;
112 | } else {
113 | return false;
114 | }
115 | }
116 |
117 | public static String getString(byte[] bytes) {
118 | try {
119 | return new String(bytes, DEFAULT_CHARSET_NAME);
120 | } catch (UnsupportedEncodingException e) {
121 | throw new RuntimeException(e);
122 | }
123 | }
124 |
125 | public static void byte2hex(byte b, StringBuffer buf) {
126 | char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
127 | 'A', 'B', 'C', 'D', 'E', 'F'};
128 | int high = ((b & 0xf0) >> 4);
129 | int low = (b & 0x0f);
130 | buf.append(hexChars[high]);
131 | buf.append(hexChars[low]);
132 | }
133 |
134 | public static void int2hex(int a, StringBuffer str) {
135 | str.append(Integer.toHexString(a));
136 | }
137 |
138 | public static void short2hex(int a, StringBuffer str) {
139 | str.append(Integer.toHexString(a));
140 | }
141 |
142 | public static void getBytes(long i, int index, byte[] buf) {
143 | long q;
144 | int r;
145 | int pos = index;
146 | byte sign = 0;
147 |
148 | if (i < 0) {
149 | sign = '-';
150 | i = -i;
151 | }
152 |
153 | // Get 2 digits/iteration using longs until quotient fits into an int
154 | while (i > Integer.MAX_VALUE) {
155 | q = i / 100;
156 | // really: r = i - (q * 100);
157 | r = (int) (i - ((q << 6) + (q << 5) + (q << 2)));
158 | i = q;
159 | buf[--pos] = DigitOnes[r];
160 | buf[--pos] = DigitTens[r];
161 | }
162 |
163 | // Get 2 digits/iteration using ints
164 | int q2;
165 | int i2 = (int) i;
166 | while (i2 >= 65536) {
167 | q2 = i2 / 100;
168 | // really: r = i2 - (q * 100);
169 | r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
170 | i2 = q2;
171 | buf[--pos] = DigitOnes[r];
172 | buf[--pos] = DigitTens[r];
173 | }
174 |
175 | // Fall thru to fast mode for smaller numbers
176 | // assert(i2 <= 65536, i2);
177 | for (; ; ) {
178 | q2 = (i2 * 52429) >>> (16 + 3);
179 | r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
180 | buf[--pos] = digits[r];
181 | i2 = q2;
182 | if (i2 == 0)
183 | break;
184 | }
185 | if (sign != 0) {
186 | buf[--pos] = sign;
187 | }
188 | }
189 |
190 | /**
191 | * Places characters representing the integer i into the character array
192 | * buf. The characters are placed into the buffer backwards starting with
193 | * the least significant digit at the specified index (exclusive), and
194 | * working backwards from there.
195 | *
196 | * Will fail if i == Integer.MIN_VALUE
197 | */
198 | static void getBytes(int i, int index, byte[] buf) {
199 | int q, r;
200 | int pos = index;
201 | byte sign = 0;
202 |
203 | if (i < 0) {
204 | sign = '-';
205 | i = -i;
206 | }
207 |
208 | // Generate two digits per iteration
209 | while (i >= 65536) {
210 | q = i / 100;
211 | // really: r = i - (q * 100);
212 | r = i - ((q << 6) + (q << 5) + (q << 2));
213 | i = q;
214 | buf[--pos] = DigitOnes[r];
215 | buf[--pos] = DigitTens[r];
216 | }
217 |
218 | // Fall thru to fast mode for smaller numbers
219 | // assert(i <= 65536, i);
220 | for (; ; ) {
221 | q = (i * 52429) >>> (16 + 3);
222 | r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
223 | buf[--pos] = digits[r];
224 | i = q;
225 | if (i == 0)
226 | break;
227 | }
228 | if (sign != 0) {
229 | buf[--pos] = sign;
230 | }
231 | }
232 |
233 | /**
234 | * All possible chars for representing a number as a String
235 | */
236 | final static byte[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
237 | '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
238 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
239 | 'z'};
240 |
241 | final static byte[] DigitTens = {'0', '0', '0', '0', '0', '0', '0', '0',
242 | '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2',
243 | '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3',
244 | '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4',
245 | '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
246 | '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7',
247 | '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8',
248 | '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9',
249 | '9',};
250 |
251 | final static byte[] DigitOnes = {'0', '1', '2', '3', '4', '5', '6', '7',
252 | '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
253 | '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
254 | '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
255 | '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
256 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
257 | '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5',
258 | '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8',
259 | '9',};
260 |
261 | final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999,
262 | 99999999, 999999999, Integer.MAX_VALUE};
263 |
264 | // Requires positive x
265 | public static final int stringSize(int x) {
266 | for (int i = 0; ; i++)
267 | if (x <= sizeTable[i])
268 | return i + 1;
269 | }
270 |
271 | // Requires positive x
272 | public static final int stringSize(long x) {
273 | long p = 10;
274 | for (int i = 1; i < 19; i++) {
275 | if (x < p)
276 | return i;
277 | p = 10 * p;
278 | }
279 | return 19;
280 | }
281 |
282 | final static int[] byte_len_array = new int[256];
283 |
284 | static {
285 | for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; ++i) {
286 | int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
287 | byte_len_array[i & 0xFF] = size;
288 | }
289 | }
290 | }
--------------------------------------------------------------------------------
/db/consistent-hash/src/main/java/io/github/flylib/consistenthash/ConsistentHash.java:
--------------------------------------------------------------------------------
1 | package io.github.flylib.consistenthash;
2 |
3 | import java.util.Set;
4 | import java.util.TreeMap;
5 |
6 | /**
7 | * @author liushaoming
8 | * 一致性hash的实现
9 | * Java implementation of consistent-hashing
10 | */
11 | public class ConsistentHash {
12 |
13 | private int virtualNum = 5; //平均虚拟节点数
14 |
15 | private HashAlgorithm alg = HashAlgorithm.KETAMA_HASH;//采用的HASH算法
16 |
17 | private Set nodeSet; //节点列表
18 |
19 | private final TreeMap nodeMap = new TreeMap();
20 |
21 | private static class SingletonHolder {
22 | private static ConsistentHash instance = new ConsistentHash();
23 | }
24 |
25 | private ConsistentHash() {
26 | }
27 |
28 | public static ConsistentHash getInstance() {
29 | return SingletonHolder.instance;
30 | }
31 |
32 | /**
33 | * 构建一致性HASH环
34 | */
35 | public void buildHashCircle() {
36 | if (nodeSet == null) return;
37 | for (Node node : nodeSet) {
38 | for (int i = 0; i < virtualNum; i++) {
39 | long nodeKey = this.alg.hash(node.toString() + "-" + i);
40 | nodeMap.put(nodeKey, node);
41 | }
42 | }
43 | }
44 |
45 | /**
46 | * 沿环的顺时针找到虚拟节点
47 | *
48 | * @param key
49 | * @return
50 | */
51 | public Node findNodeByKey(String key) {
52 | final Long hash = this.alg.hash(key);
53 | Long target = hash;
54 | if (!nodeMap.containsKey(hash)) {
55 | target = nodeMap.ceilingKey(hash);
56 | if (target == null && !nodeMap.isEmpty()) {
57 | target = nodeMap.firstKey();
58 | }
59 | }
60 | return nodeMap.get(target);
61 | }
62 |
63 | /**
64 | * 设置每个节点的虚拟节点个数,该参数默认是100
65 | *
66 | * @param virtualNum 虚拟节点数
67 | */
68 | public void setVirtualNum(int virtualNum) {
69 | this.virtualNum = virtualNum;
70 | }
71 |
72 | /**
73 | * 设置一致性HASH的算法,默认采用 KETAMA_HASH
74 | * 对于一致性HASH而言选择的HASH算法首先要考虑发散度其次再考虑性能
75 | *
76 | * @param alg 具体支持的算法
77 | * @see HashAlgorithm
78 | */
79 | public void setAlg(HashAlgorithm alg) {
80 | this.alg = alg;
81 | }
82 |
83 | /**
84 | * 配置实际的节点,允许同一个IP上多个节点,但是应该用name区分开
85 | *
86 | * @param nodeList 节点列表
87 | */
88 | public void setNodeList(Set nodeList) {
89 | this.nodeSet = nodeList;
90 | }
91 |
92 | /**
93 | * 获取环形HASH
94 | *
95 | * @return
96 | */
97 | public TreeMap getNodeMap() {
98 | return nodeMap;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/db/consistent-hash/src/main/java/io/github/flylib/consistenthash/HashAlgorithm.java:
--------------------------------------------------------------------------------
1 | package io.github.flylib.consistenthash;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.nio.ByteBuffer;
5 | import java.nio.ByteOrder;
6 | import java.security.MessageDigest;
7 | import java.security.NoSuchAlgorithmException;
8 | import java.util.zip.CRC32;
9 |
10 | public enum HashAlgorithm {
11 |
12 | /**
13 | * Native hash (String.hashCode()).
14 | */
15 | NATIVE_HASH,
16 | /**
17 | * CRC32_HASH as used by the perl API. This will be more consistent both
18 | * across multiple API users as well as java versions, but is mostly likely
19 | * significantly slower.
20 | */
21 | CRC32_HASH,
22 | /**
23 | * FNV hashes are designed to be fast while maintaining a low collision
24 | * rate. The FNV speed allows one to quickly hash lots of data while
25 | * maintaining a reasonable collision rate.
26 | *
27 | * // * @see http://www.isthe.com/chongo/tech/comp/fnv/
28 | * // * @see http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
29 | */
30 | FNV1_64_HASH,
31 | /**
32 | * Variation of FNV.
33 | */
34 | FNV1A_64_HASH,
35 | /**
36 | * 32-bit FNV1.
37 | */
38 | FNV1_32_HASH,
39 | /**
40 | * 32-bit FNV1a.
41 | */
42 | FNV1A_32_HASH,
43 | /**
44 | * MD5-based hash algorithm used by ketama.
45 | */
46 | KETAMA_HASH,
47 |
48 | /**
49 | * From mysql source
50 | */
51 | MYSQL_HASH,
52 |
53 | ELF_HASH,
54 |
55 | RS_HASH,
56 |
57 | /**
58 | * From lua source,it is used for long key
59 | */
60 | LUA_HASH,
61 | /**
62 | * MurMurHash算法,是非加密HASH算法,性能很高,
63 | * 比传统的CRC32,MD5,SHA-1(这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免)
64 | * 等HASH算法要快很多,这个算法的碰撞率很低.
65 | * http://murmurhash.googlepages.com/
66 | */
67 | MurMurHash,
68 | /**
69 | * The Jenkins One-at-a-time hash ,please see
70 | * http://www.burtleburtle.net/bob/hash/doobs.html
71 | */
72 | ONE_AT_A_TIME;
73 |
74 | private static final long FNV_64_INIT = 0xcbf29ce484222325L;
75 | private static final long FNV_64_PRIME = 0x100000001b3L;
76 |
77 | private static final long FNV_32_INIT = 2166136261L;
78 | private static final long FNV_32_PRIME = 16777619;
79 |
80 | /**
81 | * Compute the hash for the given key.
82 | *
83 | * @return a positive integer hash
84 | */
85 | public long hash(final String k) {
86 | long rv = 0;
87 | switch (this) {
88 | case NATIVE_HASH:
89 | rv = k.hashCode();
90 | break;
91 | case CRC32_HASH:
92 | // return (crc32(shift) >> 16) & 0x7fff;
93 | CRC32 crc32 = new CRC32();
94 | crc32.update(ByteUtils.getBytes(k));
95 | rv = crc32.getValue() >> 16 & 0x7fff;
96 | break;
97 | case FNV1_64_HASH: {
98 | // Thanks to pierre@demartines.com for the pointer
99 | rv = FNV_64_INIT;
100 | int len = k.length();
101 | for (int i = 0; i < len; i++) {
102 | rv *= FNV_64_PRIME;
103 | rv ^= k.charAt(i);
104 | }
105 | }
106 | break;
107 | case MurMurHash:
108 | ByteBuffer buf = ByteBuffer.wrap(k.getBytes());
109 | int seed = 0x1234ABCD;
110 |
111 | ByteOrder byteOrder = buf.order();
112 | buf.order(ByteOrder.LITTLE_ENDIAN);
113 |
114 | long m = 0xc6a4a7935bd1e995L;
115 | int r = 47;
116 |
117 | rv = seed ^ (buf.remaining() * m);
118 |
119 | long ky;
120 | while (buf.remaining() >= 8) {
121 | ky = buf.getLong();
122 |
123 | ky *= m;
124 | ky ^= ky >>> r;
125 | ky *= m;
126 |
127 | rv ^= ky;
128 | rv *= m;
129 | }
130 |
131 | if (buf.remaining() > 0) {
132 | ByteBuffer finish = ByteBuffer.allocate(8).order(
133 | ByteOrder.LITTLE_ENDIAN);
134 | // for big-endian version, do this first:
135 | // finish.position(8-buf.remaining());
136 | finish.put(buf).rewind();
137 | rv ^= finish.getLong();
138 | rv *= m;
139 | }
140 |
141 | rv ^= rv >>> r;
142 | rv *= m;
143 | rv ^= rv >>> r;
144 | buf.order(byteOrder);
145 | break;
146 | case FNV1A_64_HASH: {
147 | rv = FNV_64_INIT;
148 | int len = k.length();
149 | for (int i = 0; i < len; i++) {
150 | rv ^= k.charAt(i);
151 | rv *= FNV_64_PRIME;
152 | }
153 | }
154 | break;
155 | case FNV1_32_HASH: {
156 | rv = FNV_32_INIT;
157 | int len = k.length();
158 | for (int i = 0; i < len; i++) {
159 | rv *= FNV_32_PRIME;
160 | rv ^= k.charAt(i);
161 | }
162 | }
163 | break;
164 | case FNV1A_32_HASH: {
165 | rv = FNV_32_INIT;
166 | int len = k.length();
167 | for (int i = 0; i < len; i++) {
168 | rv ^= k.charAt(i);
169 | rv *= FNV_32_PRIME;
170 | }
171 | }
172 | break;
173 | case KETAMA_HASH:
174 | byte[] bKey = computeMd5(k);
175 | rv = (long) (bKey[3] & 0xFF) << 24 | (long) (bKey[2] & 0xFF) << 16
176 | | (long) (bKey[1] & 0xFF) << 8 | bKey[0] & 0xFF;
177 | break;
178 |
179 | case MYSQL_HASH:
180 | int nr2 = 4;
181 | for (int i = 0; i < k.length(); i++) {
182 | rv ^= ((rv & 63) + nr2) * k.charAt(i) + (rv << 8);
183 | nr2 += 3;
184 | }
185 | break;
186 | case ELF_HASH:
187 | long x = 0;
188 | for (int i = 0; i < k.length(); i++) {
189 | rv = (rv << 4) + k.charAt(i);
190 | if ((x = rv & 0xF0000000L) != 0) {
191 | rv ^= x >> 24;
192 | rv &= ~x;
193 | }
194 | }
195 | rv = rv & 0x7FFFFFFF;
196 | break;
197 | case RS_HASH:
198 | long b = 378551;
199 | long a = 63689;
200 | for (int i = 0; i < k.length(); i++) {
201 | rv = rv * a + k.charAt(i);
202 | a *= b;
203 | }
204 | rv = rv & 0x7FFFFFFF;
205 | break;
206 | case LUA_HASH:
207 | int step = (k.length() >> 5) + 1;
208 | rv = k.length();
209 | for (int len = k.length(); len >= step; len -= step) {
210 | rv = rv ^ (rv << 5) + (rv >> 2) + k.charAt(len - 1);
211 | }
212 | case ONE_AT_A_TIME:
213 | try {
214 | int hash = 0;
215 | for (byte bt : k.getBytes("utf-8")) {
216 | hash += (bt & 0xFF);
217 | hash += (hash << 10);
218 | hash ^= (hash >>> 6);
219 | }
220 | hash += (hash << 3);
221 | hash ^= (hash >>> 11);
222 | hash += (hash << 15);
223 | return hash;
224 | } catch (UnsupportedEncodingException e) {
225 | throw new IllegalStateException("Hash function error", e);
226 | }
227 | default:
228 | assert false;
229 | }
230 |
231 | return rv & 0xffffffffL; /* Truncate to 32-bits */
232 | }
233 |
234 | /**
235 | * Get the md5 of the given key.
236 | */
237 | public static byte[] computeMd5(String k) {
238 | MessageDigest md5;
239 | try {
240 | md5 = MessageDigest.getInstance("MD5");
241 | } catch (NoSuchAlgorithmException e) {
242 | throw new RuntimeException("MD5 not supported", e);
243 | }
244 | md5.reset();
245 | md5.update(ByteUtils.getBytes(k));
246 | return md5.digest();
247 | }
248 |
249 | // public static void main(String[] args) {
250 | // HashAlgorithm alg=HashAlgorithm.LUA_HASH;
251 | // long h=0;
252 | // long start=System.currentTimeMillis();
253 | // for(int i=0;i<100000;i++) {
254 | // h=alg.hash("dddddd");
255 | //// System.out.println(h);
256 | // }
257 | // System.out.println(System.currentTimeMillis()-start);
258 | // }
259 | }
--------------------------------------------------------------------------------
/db/consistent-hash/src/main/java/io/github/flylib/consistenthash/Node.java:
--------------------------------------------------------------------------------
1 | package io.github.flylib.consistenthash;
2 |
3 | /**
4 | * 节点的IP实现
5 | */
6 | public class Node {
7 |
8 | private String name;
9 |
10 | private String ip;
11 |
12 | public Node(String name, String ip) {
13 | this.name = name;
14 | this.ip = ip;
15 | }
16 |
17 | public Node(String ip) {
18 | this.ip = ip;
19 | }
20 |
21 | public String getName() {
22 | return name;
23 | }
24 |
25 | public void setName(String name) {
26 | this.name = name;
27 | }
28 |
29 | public String getIp() {
30 | return ip;
31 | }
32 |
33 | public void setIp(String ip) {
34 | this.ip = ip;
35 | }
36 |
37 | @Override
38 | public String toString() {
39 |
40 | if (name != null && !"".equals(name)) {
41 | return ip + "-" + name;
42 | }
43 | return ip;
44 | }
45 |
46 | @Override
47 | public boolean equals(Object o) {
48 | if (o == null) return false;
49 | Node node = (Node) o;
50 | if (node.getIp() == null && ip == null && node.getName() == null && name == null) return true;
51 | if (name == null && node.getName() != null) return false;
52 | if (ip == null && node.getIp() != null) return false;
53 | assert ip != null;
54 | assert name != null;
55 | return name.equals(node.getName()) && ip.equals(node.getIp());
56 | }
57 |
58 | @Override
59 | public int hashCode() {
60 | int result = name != null ? name.hashCode() : 0;
61 | result = 31 * result + (ip != null ? ip.hashCode() : 0);
62 | return result;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/db/consistent-hash/src/test/java/TestConsistentHash.java:
--------------------------------------------------------------------------------
1 |
2 | import io.github.flylib.consistenthash.ConsistentHash;
3 | import io.github.flylib.consistenthash.Node;
4 |
5 | import java.util.*;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | /**
9 | * @author liushaoming
10 | * 一致性HASH测试--高并发测试
11 | */
12 | public class TestConsistentHash {
13 | final ConcurrentHashMap stat = new ConcurrentHashMap();
14 |
15 | public static void main(String[] args) throws InterruptedException {
16 | final TestConsistentHash testConsistHashWithComp = new TestConsistentHash();
17 | Set ips = new HashSet();
18 | ips.add(new Node("192.168.10.1"));
19 | ips.add(new Node("192.168.10.2"));
20 | ips.add(new Node("192.168.10.3"));
21 | ips.add(new Node("192.168.10.4"));
22 | ips.add(new Node("192.168.10.5"));
23 | ips.add(new Node("192.168.10.6"));
24 | ips.add(new Node("192.168.10.7"));
25 | ips.add(new Node("192.168.10.8"));
26 | ips.add(new Node("192.168.10.9"));
27 | ips.add(new Node("192.168.1.10"));
28 |
29 | final ConsistentHash hash = ConsistentHash.getInstance();
30 | hash.setNodeList(ips);
31 | // hash.setAlg(HashAlgorithm.LUA_HASH);
32 | // hash.set(1024);
33 | // hash.setAlg(HashAlgorithm.CRC32_HASH);
34 | hash.buildHashCircle();
35 |
36 | long start = System.currentTimeMillis();
37 |
38 | // for (int i = 0; i < 20000; i++) {
39 | for (int i = 0; i < 500; i++) {
40 | final String name = "thread" + i;
41 | Thread t = new Thread(new Runnable() {
42 | @Override
43 | public void run() {
44 | // for (int h = 0; h < 1000; h++) {
45 | for (int h = 0; h < 2; h++) {
46 | Node node = hash.findNodeByKey(name + h);
47 | testConsistHashWithComp.send(node);
48 | }
49 | testConsistHashWithComp.print();
50 | }
51 | }, name);
52 | t.start();
53 | }
54 | System.out.println(System.currentTimeMillis() - start);
55 | Thread.sleep(1000 * 20);
56 | testConsistHashWithComp.print();
57 | }
58 |
59 | public synchronized void send(Node node) {
60 | Long count = stat.get(node.getIp());
61 | if (count == null) {
62 | stat.put(node.getIp(), 1L);
63 | } else {
64 | stat.put(node.getIp(), count + 1);
65 | }
66 | }
67 |
68 | public ConcurrentHashMap getStat() {
69 | return stat;
70 | }
71 |
72 | public void print() {
73 | long all = 0;
74 | for (Map.Entry entry : stat.entrySet()) {
75 | long num = entry.getValue();
76 | all += num;
77 | System.out.println("mac:" + entry.getKey() + " hits:" + num);
78 | }
79 | System.out.println("all:" + all);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.appjishu
8 | distarch-jdbc
9 | 1.0-SNAPSHOT
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.1.0.RELEASE
15 |
16 |
17 |
18 |
19 | UTF-8
20 | UTF-8
21 | 1.8
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-web
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-test
32 | test
33 |
34 |
35 | mysql
36 | mysql-connector-java
37 | 8.0.11
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/config/.gitignore:
--------------------------------------------------------------------------------
1 | DataSourceConfigConst
2 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/config/DataSourceConfigConst.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.config;
2 |
3 | public class DataSourceConfigConst {
4 | public static final String DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver";
5 | public static final String URL = "jdbc:mysql://localhost:3306/mall?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false";
6 | public static final String USERNAME = "root";
7 | public static final String PASSWORD = "yourPassword";
8 |
9 |
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/JdbcApp.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.demo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class JdbcApp {
8 | public static void main(String[] args){
9 | SpringApplication.run(JdbcApp.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.demo.controller;
2 |
3 | import com.appjishu.jdbc.demo.service.DemoService;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RequestMapping("")
9 | @RestController
10 | public class HomeController {
11 | @Autowired
12 | private DemoService demoService;
13 |
14 | @RequestMapping("")
15 | public String index() {
16 | return "Access sword-springboot OK.";
17 | }
18 |
19 | @RequestMapping("/test")
20 | public String test() {
21 | demoService.handle();
22 | return "Test done.";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/dao/ProductDAO.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.demo.dao;
2 |
3 | import com.appjishu.jdbc.util.ConnectionManager;
4 | import org.springframework.stereotype.Repository;
5 |
6 | import java.sql.Connection;
7 | import java.sql.ResultSet;
8 | import java.sql.SQLException;
9 | import java.sql.Statement;
10 | import java.util.Random;
11 |
12 | @Repository
13 | public class ProductDAO {
14 | public int add() throws SQLException {
15 | Random random = new Random();
16 | String sql = "insert into product (name, price) values ('iphone xr', " + random.nextInt(100) + " );";
17 | Connection conn = ConnectionManager.getConnection();
18 | Statement statement = conn.createStatement();
19 | int updatedCount = statement.executeUpdate(sql);
20 | return updatedCount;
21 | }
22 |
23 | public int count() throws SQLException {
24 | String sql = "SELECT COUNT(1) FROM product ";
25 | Connection connection = ConnectionManager.getConnection();
26 | Statement statement = connection.createStatement();
27 | ResultSet resultSet = statement.executeQuery(sql);
28 | return resultSet.next() ? resultSet.getInt(1) : -1;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/dao/UserDAO.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.demo.dao;
2 |
3 | import com.appjishu.jdbc.util.ConnectionManager;
4 | import org.springframework.stereotype.Repository;
5 |
6 | import java.sql.Connection;
7 | import java.sql.SQLException;
8 | import java.sql.Statement;
9 | import java.util.Random;
10 |
11 | @Repository
12 | public class UserDAO {
13 |
14 | public int add() throws SQLException {
15 | /**
16 | * user_table表不存在,会抛异常
17 | */
18 | Random random = new Random();
19 | String sql ="insert into user_table (name, price) values ('iphone xr', " + random.nextInt(100) + " );";
20 | Connection conn = ConnectionManager.getConnection();
21 | Statement statement = conn.createStatement();
22 | int updatedCount =statement.executeUpdate(sql);
23 | return updatedCount;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/service/DemoService.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.demo.service;
2 |
3 | import com.appjishu.jdbc.demo.dao.ProductDAO;
4 | import com.appjishu.jdbc.demo.dao.UserDAO;
5 | import com.appjishu.jdbc.util.ConnectionManager;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Service;
10 |
11 | import java.sql.Connection;
12 | import java.sql.SQLException;
13 |
14 | @Service
15 | public class DemoService {
16 | private static final Logger log = LoggerFactory.getLogger(DemoService.class);
17 |
18 | @Autowired
19 | private ProductDAO productDAO;
20 | @Autowired
21 | private UserDAO userDAO;
22 |
23 |
24 | public void handle() {
25 | Connection connection = ConnectionManager.getConnection();
26 |
27 | try {
28 | int count0 = productDAO.count();
29 | log.info("count0={}", count0);
30 | ConnectionManager.beginTransaction(connection);
31 | productDAO.add();
32 | int count1 = productDAO.count();
33 | log.info("count1={}", count1);
34 | boolean test = true;
35 | if (test) {
36 | userDAO.add();
37 | }
38 | ConnectionManager.commitTransaction(connection);
39 | } catch (Exception e) {
40 | e.printStackTrace();
41 | ConnectionManager.rollbackTransaction(connection);
42 | }
43 | int count2 = 0;
44 | boolean nowAutoCommit = false;
45 | try {
46 | count2 = productDAO.count();
47 | nowAutoCommit = connection.getAutoCommit();
48 | } catch (SQLException e) {
49 | e.printStackTrace();
50 | }
51 | log.info("getAutoCommit()={}", nowAutoCommit);
52 | log.info("count2={}", count2);
53 |
54 | log.info("---Done---");
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/util/ConnectionManager.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.util;
2 |
3 | import java.sql.Connection;
4 | import java.sql.SQLException;
5 |
6 | public class ConnectionManager {
7 | private static ThreadLocal connectionHolder = new ThreadLocal();
8 |
9 | /**
10 | * 采用ThreadLocal封装Connection
11 | *
12 | * @return
13 | */
14 | public static Connection getConnection() {
15 | //获得线程变量connectionHolder的值conn
16 | Connection conn = connectionHolder.get();
17 | if (conn == null) {
18 | //如果连接为空,则创建连接,另一个工具类,创建连接
19 | conn = DBUtil.getConnection();
20 | //将局部变量connectionHolder的值设置为conn
21 | connectionHolder.set(conn);
22 | }
23 | return conn;
24 | }
25 |
26 |
27 | /**
28 | * 关闭连接和从线程变量中删除conn
29 | */
30 | public static void closeConnection() {
31 | //获得线程变量connectionHolder的值conn
32 | Connection conn = connectionHolder.get();
33 | if (conn != null) {
34 | try {
35 | //关闭连接
36 | conn.close();
37 | //从线程局部变量中移除conn,如果没有移除掉,下次还会用这个已经关闭的连接,就会出错
38 | connectionHolder.remove();
39 | } catch (SQLException e) {
40 | e.printStackTrace();
41 | }
42 | }
43 | }
44 |
45 | /**
46 | *开启事务,手动开启
47 | */
48 | public static void beginTransaction(Connection conn){
49 | try {
50 | //如果连接存在,再设置连接,否则会出错
51 | if (conn != null){
52 | //默认conn是自动提交,
53 | if (conn.getAutoCommit()){
54 | //关闭自动提交,即是手动开启事务
55 | conn.setAutoCommit(false);
56 | }
57 | }
58 | }catch(SQLException e){
59 | e.printStackTrace();
60 | }
61 | }
62 |
63 | /**
64 | * 提交事务
65 | */
66 | public static void commitTransaction(Connection conn){
67 | try{
68 | if (conn != null){
69 | if (!conn.getAutoCommit()){
70 | conn.commit();
71 | }
72 | }
73 | }catch(SQLException e){
74 | e.printStackTrace();
75 | }
76 | }
77 |
78 | /**
79 | * 回滚事务
80 | */
81 | public static void rollbackTransaction(Connection conn){
82 | try {
83 | if (conn != null){
84 | if(!conn.getAutoCommit()){
85 | conn.rollback();
86 | }
87 | }
88 | }catch(SQLException e){
89 | e.printStackTrace();
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/java/com/appjishu/jdbc/util/DBUtil.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.jdbc.util;
2 |
3 | import com.appjishu.jdbc.config.DataSourceConfigConst;
4 | import com.mysql.cj.jdbc.MysqlConnectionPoolDataSource;
5 | import com.mysql.cj.jdbc.MysqlDataSource;
6 |
7 | import javax.sql.DataSource;
8 | import java.sql.Connection;
9 | import java.sql.DriverManager;
10 | import java.sql.SQLException;
11 |
12 | public class DBUtil {
13 | public static Connection getConnection() {
14 | Connection connection = null;
15 | try {
16 | Class.forName(DataSourceConfigConst.DRIVER_CLASS_NAME);
17 |
18 | MysqlDataSource mysqlDataSource = new MysqlConnectionPoolDataSource();
19 | mysqlDataSource.setUrl(DataSourceConfigConst.URL);
20 | mysqlDataSource.setUser(DataSourceConfigConst.USERNAME);
21 | mysqlDataSource.setPassword(DataSourceConfigConst.PASSWORD);
22 | connection = mysqlDataSource.getConnection();
23 | } catch (SQLException e) {
24 | e.printStackTrace();
25 | connection = null;
26 | } catch (ClassNotFoundException e1) {
27 | e1.printStackTrace();
28 | connection = null;
29 | }
30 | return connection;
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/db/distarch-jdbc/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=15001
2 |
--------------------------------------------------------------------------------
/doc/image/group-qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bootsrc/distarch/224a27cc1cf7df6ac80807c97c95a2ff8d06a61b/doc/image/group-qrcode.png
--------------------------------------------------------------------------------
/doc/image/public-account.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bootsrc/distarch/224a27cc1cf7df6ac80807c97c95a2ff8d06a61b/doc/image/public-account.jpg
--------------------------------------------------------------------------------
/encrypt/distarch-encrypt/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.liushaoming.distarch
8 | distarch-encrypt
9 | 1.0-SNAPSHOT
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.1.0.RELEASE
15 |
16 |
17 |
18 |
19 | UTF-8
20 | UTF-8
21 | 1.8
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-web
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-test
32 | test
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/encrypt/distarch-encrypt/src/main/java/com/liushaoming/encrypt/EncryptTest.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.encrypt;
2 |
3 | /**
4 | * 右键--run as java application此class即可调试
5 | * 使用md5加盐加密。不可逆加密。
6 | */
7 | public class EncryptTest {
8 | public static void main(String[] args){
9 | testEncrypt();
10 | }
11 |
12 | private static void testEncrypt() {
13 | System.out.println("You should save code in deploy database. And sell license to customers.");
14 |
15 | for (int i=0;i< 5; i++) {
16 | String license = UUIDUtil.newUuid();
17 | String code = EncryptUtil.encrypt(license);
18 |
19 | System.out.println("license_" + i + "=" + license + ", code=" + code);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/encrypt/distarch-encrypt/src/main/java/com/liushaoming/encrypt/EncryptUtil.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.encrypt;
2 |
3 | import org.springframework.util.DigestUtils;
4 | import org.springframework.util.StringUtils;
5 |
6 | import java.security.InvalidParameterException;
7 |
8 | public class EncryptUtil {
9 | private static final String SALT = "nnjskslsl@#$*&!ap";
10 |
11 | /**
12 | * 加密函数: 计算公式, code = encrypt(license)
13 | * @param license
14 | * @return
15 | */
16 | public static String encrypt(String license) {
17 | if (StringUtils.isEmpty(license)) {
18 | throw new InvalidParameterException("license can not be null or empty");
19 | }
20 | return DigestUtils.md5DigestAsHex(license.getBytes());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/encrypt/distarch-encrypt/src/main/java/com/liushaoming/encrypt/UUIDUtil.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.encrypt;
2 |
3 | import java.util.UUID;
4 |
5 | public class UUIDUtil {
6 |
7 |
8 | // public static void main(String[] args) {
9 | // String uuid = newUuid();
10 | // System.out.println("uuid=" + uuid);
11 | // }
12 |
13 | /**
14 | * UUID是由一个十六位的数字组成,表现出来的形式例如
15 | * 668eee3274eb4873a1b921b7f9bb85d6
16 | * @return
17 | */
18 | public static String newUuid() {
19 | return UUID.randomUUID().toString().replace("-", "");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lock/distarch-lock-curator/.gitignore:
--------------------------------------------------------------------------------
1 | # maven ignore
2 | target/
3 | *.jar
4 | *.war
5 | *.zip
6 | *.tar
7 |
8 | # eclipse ignore
9 | .settings/
10 | .project
11 | .classpath
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | logs/
21 | *.doc
22 | *.log
23 | *.cache
24 | *.diff
25 | *.patch
26 | *.tmp
27 |
28 | # system ignore
29 | .DS_Store
30 | Thumbs.db
31 |
--------------------------------------------------------------------------------
/lock/distarch-lock-curator/README.md:
--------------------------------------------------------------------------------
1 | # distarch-lock-curator
2 |
3 | 基于apache curator框架来实现分布式锁
4 |
5 | 在 JDK 的 java.util.concurrent.locks 中, 为我们提供了可重入锁, 读写锁,
6 |
7 | 及超时获取锁的方法. 为我们提供了完好的支持, 但是在分布式系统中, 当多个应用需
8 |
9 | 要共同操作某一个资源时. 我么就无法使用 JDK 来实现了, 这时就需要使用一个外部
10 |
11 | 服务来为此进行支持, 现在我们选用 ZooKeeper + Curator 来完成分布式锁
12 |
13 |
14 | ## 演示方法
15 |
16 | 导入项目到eclipse/idea,然后选中LockCuratorTest.java 右键--run as -java application
17 |
18 | 即可运行测试.
19 |
20 | ## 实现原理
21 |
22 | 使用curator里面的可重入分布式锁InterProcessMutex
23 | LockCuratorTest代码如下
24 |
25 | ```java
26 |
27 | CuratorHolder curatorHolder = new CuratorHolder();
28 | CuratorFramework client = curatorHolder.getClient();
29 |
30 | if (client.getState() != CuratorFrameworkState.STARTED) {
31 | client.start();
32 | }
33 | InterProcessLock lock = new InterProcessMutex(client, lockRoot);
34 |
35 | try {
36 | lockSuccess = lock.acquire(1, TimeUnit.SECONDS);
37 | } catch (Exception e) {
38 | e.printStackTrace();
39 | }
40 |
41 | logger.info(">>>>>threadId={}, lock_success={}, threadIndex={}",
42 | new Object[]{threadId, lockSuccess, index + ""});
43 |
44 |
45 | if (lockSuccess) {
46 | try {
47 | Thread.sleep(RandomUtils.nextInt(10, 20));
48 | lock.release();
49 | logger.info(">>>>>threadId={}, lock_released={}, threadIndex={}",
50 | new Object[]{threadId, lockSuccess, index});
51 | } catch (Exception e) {
52 | e.printStackTrace();
53 |
54 | }
55 | }
56 | ```
57 |
58 | 其中curatorHolder.getClient()
使用了ThreadLocal来实现每个线程中都各自拥有一个独立的CuratorFramework
59 |
60 | 实例。在多线程环境下(比如多线程环境下),有效减小CuratorFramework实例的个数.
61 |
--------------------------------------------------------------------------------
/lock/distarch-lock-curator/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.liushaoming
8 | distarch-lock-curator
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | org.springframework.boot
14 | spring-boot-starter-parent
15 | 2.1.2.RELEASE
16 |
17 |
18 |
19 |
20 | 1.8
21 | UTF-8
22 | UTF-8
23 |
24 |
25 |
26 |
27 | org.apache.zookeeper
28 | zookeeper
29 | 3.4.13
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-web
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-test
38 | test
39 |
40 |
41 |
42 | org.apache.curator
43 | curator-recipes
44 | 4.1.0
45 |
46 |
47 |
48 | org.apache.commons
49 | commons-lang3
50 | 3.8.1
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | org.apache.curator
59 | curator-recipes
60 |
61 |
62 | org.apache.zookeeper
63 | zookeeper
64 |
65 |
66 |
67 |
68 |
69 | org.apache.zookeeper
70 | zookeeper
71 |
72 |
73 | org.slf4j
74 | slf4j-log4j12
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | distlockboot
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-maven-plugin
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/lock/distarch-lock-curator/src/main/java/com/liushaoming/lock/curator/CuratorHolder.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.lock.curator;
2 |
3 | import org.apache.curator.RetryPolicy;
4 | import org.apache.curator.framework.CuratorFramework;
5 | import org.apache.curator.framework.CuratorFrameworkFactory;
6 | import org.apache.curator.retry.ExponentialBackoffRetry;
7 |
8 | public class CuratorHolder {
9 |
10 | public void setZkConfigBean(ZKConfigBean zkConfigBean) {
11 | this.zkConfigBean = zkConfigBean;
12 | }
13 |
14 | private ZKConfigBean zkConfigBean;
15 |
16 | private final ThreadLocal lockHolder = new ThreadLocal() {
17 | @Override
18 | protected CuratorFramework initialValue() {
19 | return newClient();
20 | }
21 | };
22 |
23 | private CuratorFramework newClient(){
24 | RetryPolicy retry = new ExponentialBackoffRetry(1000, 3);
25 | return CuratorFrameworkFactory.builder().connectString(zkConfigBean.getConnectStr())
26 | .retryPolicy(retry)
27 | .sessionTimeoutMs(zkConfigBean.getSessionTimeout())
28 | .connectionTimeoutMs(zkConfigBean.getConnectTimeout())
29 | .build();
30 | }
31 |
32 | public CuratorFramework getClient() {
33 | CuratorFramework client = lockHolder.get();
34 | if (client == null) {
35 | client = newClient();
36 | lockHolder.set(client);
37 | }
38 | return client;
39 | }
40 |
41 | public void setClient(CuratorFramework client) {
42 | lockHolder.set(client);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lock/distarch-lock-curator/src/main/java/com/liushaoming/lock/curator/LockCuratorTest.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.lock.curator;
2 |
3 | import org.apache.commons.lang3.RandomUtils;
4 | import org.apache.curator.framework.CuratorFramework;
5 | import org.apache.curator.framework.imps.CuratorFrameworkState;
6 | import org.apache.curator.framework.recipes.locks.InterProcessLock;
7 | import org.apache.curator.framework.recipes.locks.InterProcessMutex;
8 | import org.apache.curator.utils.CloseableUtils;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 | import java.util.concurrent.TimeUnit;
15 |
16 | public class LockCuratorTest {
17 | private static final Logger logger = LoggerFactory.getLogger(LockCuratorTest.class);
18 | static int index = 0;
19 |
20 | public static void main(String[] args) {
21 |
22 | final int threadCount = 20;
23 | CuratorHolder curatorHolder = new CuratorHolder();
24 | ZKConfigBean zkConfigBean = new ZKConfigBean();
25 | int sessionTimeout = 10000;
26 | String connectionStr = "localhost:2181";
27 | String lockRoot = "/lock_curator_test";
28 | int connectTimeout = 8000;
29 |
30 | zkConfigBean.setConnectStr(connectionStr);
31 | zkConfigBean.setLockRoot(lockRoot);
32 | zkConfigBean.setSessionTimeout(sessionTimeout);
33 | zkConfigBean.setConnectTimeout(connectTimeout);
34 | curatorHolder.setZkConfigBean(zkConfigBean);
35 |
36 | ExecutorService threadPool = Executors.newFixedThreadPool(3);
37 | for (int i = 0; i < threadCount; i++) {
38 | Thread thread = new Thread(new Runnable() {
39 | public void run() {
40 | index++;
41 | CuratorFramework client = curatorHolder.getClient();
42 | if (client.getState() != CuratorFrameworkState.STARTED) {
43 | client.start();
44 | }
45 | InterProcessLock lock = new InterProcessMutex(client, lockRoot);
46 | boolean lockSuccess = false;
47 |
48 | long threadId = Thread.currentThread().getId();
49 | logger.info("---tringToAcquire,thredId={}", threadId);
50 |
51 | try {
52 | lockSuccess = lock.acquire(1, TimeUnit.SECONDS);
53 | } catch (Exception e) {
54 | e.printStackTrace();
55 | }
56 |
57 | logger.info(">>>>>threadId={}, lock_success={}, threadIndex={}",
58 | new Object[]{threadId, lockSuccess, index + ""});
59 |
60 |
61 | if (lockSuccess) {
62 | try {
63 | Thread.sleep(RandomUtils.nextInt(10, 20));
64 | lock.release();
65 | logger.info(">>>>>threadId={}, lock_released={}, threadIndex={}",
66 | new Object[]{threadId, lockSuccess, index});
67 | } catch (Exception e) {
68 | e.printStackTrace();
69 |
70 | }
71 | }
72 |
73 | // if (client.getState() == CuratorFrameworkState.STARTED) {
74 | // logger.info("---Stop CuratorClient---,thredId={}", threadId);
75 | // CloseableUtils.closeQuietly(client);
76 | // }
77 |
78 |
79 | CloseableUtils.closeQuietly(client);
80 | }
81 | });
82 |
83 | // threadPool.submit(thread);
84 | thread.start();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lock/distarch-lock-curator/src/main/java/com/liushaoming/lock/curator/ZKConfigBean.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.lock.curator;
2 |
3 | public class ZKConfigBean {
4 | private String lockRoot;
5 | private int sessionTimeout;
6 | private String connectStr;
7 | private int connectTimeout;
8 |
9 | public String getLockRoot() {
10 | return lockRoot;
11 | }
12 |
13 | public void setLockRoot(String lockRoot) {
14 | this.lockRoot = lockRoot;
15 | }
16 |
17 | public int getSessionTimeout() {
18 | return sessionTimeout;
19 | }
20 |
21 | public void setSessionTimeout(int sessionTimeout) {
22 | this.sessionTimeout = sessionTimeout;
23 | }
24 |
25 | public String getConnectStr() {
26 | return connectStr;
27 | }
28 |
29 | public void setConnectStr(String connectStr) {
30 | this.connectStr = connectStr;
31 | }
32 |
33 | public int getConnectTimeout() {
34 | return connectTimeout;
35 | }
36 |
37 | public void setConnectTimeout(int connectTimeout) {
38 | this.connectTimeout = connectTimeout;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lock/distarch-lock-zookeeper/.gitignore:
--------------------------------------------------------------------------------
1 | # maven ignore
2 | target/
3 | *.jar
4 | *.war
5 | *.zip
6 | *.tar
7 |
8 | # eclipse ignore
9 | .settings/
10 | .project
11 | .classpath
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | logs/
21 | *.doc
22 | *.log
23 | *.cache
24 | *.diff
25 | *.patch
26 | *.tmp
27 |
28 | # system ignore
29 | .DS_Store
30 | Thumbs.db
31 |
--------------------------------------------------------------------------------
/lock/distarch-lock-zookeeper/README.md:
--------------------------------------------------------------------------------
1 | # distarch-lock-zookeeper
2 |
3 | 其实基于ZooKeeper,就是使用它的临时有序节点来实现的分布式锁。
4 |
5 | 原理就是:当某客户端要进行逻辑的加锁时,就在zookeeper上的某个指定节点的目录下,去生成一个唯一的临时有序节点,
6 |
7 | 然后判断自己是否是这些有序节点中序号最小的一个,如果是,则算是获取了锁。如果不是,则说明没有获取到锁,那么就需
8 |
9 | 要在序列中找到比自己小的那个节点,并对其调用exist()方法,对其注册事件监听,当监听到这个节点被删除了,那就再去
10 |
11 | 判断一次自己当初创建的节点是否变成了序列中最小的。如果是,则获取锁,如果不是,则重复上述步骤。
12 |
13 | 当释放锁的时候,只需将这个临时节点删除即可。
14 |
--------------------------------------------------------------------------------
/lock/distarch-lock-zookeeper/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.liushaoming.distarch
8 | distarch-lock-zookeeper
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | org.apache.zookeeper
14 | zookeeper
15 | 3.4.13
16 |
17 |
18 | org.apache.commons
19 | commons-lang3
20 | 3.8.1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/lock/distarch-lock-zookeeper/src/main/java/com/liushaoming/lock/zk/DistributedLock.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.lock.zk;
2 |
3 | import org.apache.commons.lang3.RandomUtils;
4 | import org.apache.zookeeper.*;
5 | import org.apache.zookeeper.data.Stat;
6 |
7 | import java.io.IOException;
8 | import java.util.List;
9 | import java.util.SortedSet;
10 | import java.util.TreeSet;
11 | import java.util.concurrent.CountDownLatch;
12 | import java.util.concurrent.TimeUnit;
13 |
14 | /**
15 | * Created by massive on 2016-12-15.
16 | */
17 | public class DistributedLock {
18 |
19 | private String lockId;
20 |
21 | //--------------------------------------------------------------
22 | // data为存储的节点数据内容
23 | // 由于锁机制用的是序列功能的特性,data的值不重要,只要利于网络传输即可
24 | //--------------------------------------------------------------
25 | private final static byte[] data = {0x12, 0x34};
26 |
27 | private final CountDownLatch latch = new CountDownLatch(1);
28 |
29 | private ZooKeeper zk;
30 |
31 | private String lockRoot = "";
32 | private int sessionTimeout = 0;
33 | private String connectionString = "0";
34 |
35 | public DistributedLock(ZooKeeper zk, int sessionTimeout) {
36 | this.zk = zk;
37 | this.sessionTimeout = sessionTimeout;
38 | }
39 |
40 | public DistributedLock(String lockRoot, String connectionString, int sessionTimeout) throws IOException, KeeperException, InterruptedException {
41 | this.lockRoot = lockRoot;
42 | this.connectionString = connectionString;
43 | this.sessionTimeout = sessionTimeout;
44 | this.zk = ZookeeperClient.getInstance(connectionString, sessionTimeout);
45 | }
46 |
47 | class LockWatcher implements Watcher {
48 | @Override
49 | public void process(WatchedEvent event) {
50 | //--------------------------------------------------------------
51 | // 监控节点变化(本程序为序列的上一节点)
52 | // 若为节点删除,证明序列的上一节点已删除,此时释放阀门让当前的lock获得锁
53 | //--------------------------------------------------------------
54 | if (event.getType() == Event.EventType.NodeDeleted)
55 | latch.countDown();
56 | }
57 | }
58 |
59 | /**
60 | * @return
61 | * @throws KeeperException
62 | * @throws InterruptedException
63 | */
64 | public synchronized boolean lock() {
65 |
66 | //--------------------------------------------------------------
67 | // 保证锁根节点存在,若不存在则创建它
68 | //--------------------------------------------------------------
69 | createLockRootIfNotExists();
70 |
71 | try {
72 |
73 | lockId = zk.create(lockRoot + "/", data,
74 | ZooDefs.Ids.OPEN_ACL_UNSAFE,
75 | CreateMode.EPHEMERAL_SEQUENTIAL);
76 |
77 | System.out.println("thread " + Thread.currentThread().getName() +
78 | " create the lock node: " + lockId + ", trying to get lock now");
79 |
80 | //--------------------------------------------------------------
81 | // 获得锁根节点下的各锁子节点,并排序
82 | //--------------------------------------------------------------
83 | List nodes = zk.getChildren(lockRoot, true);
84 | SortedSet sortedNode = new TreeSet();
85 |
86 | for (String node : nodes) {
87 | sortedNode.add(lockRoot + "/" + node);
88 | }
89 |
90 | String first = sortedNode.first();
91 | SortedSet lessThanMe = sortedNode.headSet(lockId);
92 |
93 | long threadId = Thread.currentThread().getId();
94 | //--------------------------------------------------------------
95 | // 检查是否有比当前锁节点lockId更小的节点,若有则监控当前节点的前一节点
96 | //--------------------------------------------------------------
97 | if (lockId.equals(first)) {
98 | System.out.println(">>>lockSuccess,lockId=" + lockId + ", threadId=" + threadId);
99 | return true;
100 | } else if (!lessThanMe.isEmpty()) {
101 | String prevLockId = lessThanMe.last();
102 | zk.exists(prevLockId, new LockWatcher());
103 | //--------------------------------------------------------------
104 | // 阀门等待sessionTimeout的时间
105 | // 当等待sessionTimeout的时间过后,上一个lock的Zookeeper连接会过期,删除所有临时节点,触发监听器
106 | //--------------------------------------------------------------
107 | latch.await(sessionTimeout, TimeUnit.MILLISECONDS);
108 | System.out.println(">>>lockSuccess,lockId=" + lockId + ", threadId=" + threadId);
109 | }
110 |
111 |
112 | } catch (KeeperException e) {
113 | e.printStackTrace();
114 | } catch (InterruptedException e) {
115 | e.printStackTrace();
116 | }
117 |
118 | return true;
119 | }
120 |
121 |
122 | public synchronized boolean unlock() {
123 | long threadId = Thread.currentThread().getId();
124 | //--------------------------------------------------------------
125 | // 删除lockId节点以释放锁
126 | //--------------------------------------------------------------
127 | try {
128 | System.out.println(">>>UnlockSuccess,lockId=" + lockId + ", threadId=" + threadId);
129 | zk.delete(lockId, -1);
130 | return true;
131 | } catch (InterruptedException e) {
132 | e.printStackTrace();
133 | } catch (KeeperException e) {
134 | e.printStackTrace();
135 | } finally {
136 | try {
137 | zk.close();
138 | } catch (InterruptedException e) {
139 | e.printStackTrace();
140 | }
141 | }
142 | return false;
143 | }
144 |
145 | /**
146 | * 保证锁根节点存在,若不存在则创建它
147 | */
148 | public void createLockRootIfNotExists() {
149 | try {
150 | Stat stat = zk.exists(lockRoot, false);
151 | if (stat == null) {
152 | zk.create(lockRoot, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
153 | }
154 | } catch (KeeperException e) {
155 | e.printStackTrace();
156 | } catch (InterruptedException e) {
157 | e.printStackTrace();
158 | }
159 | }
160 |
161 | }
162 |
163 |
--------------------------------------------------------------------------------
/lock/distarch-lock-zookeeper/src/main/java/com/liushaoming/lock/zk/ZkTest.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.lock.zk;
2 |
3 | import org.apache.commons.lang3.RandomUtils;
4 | import org.apache.zookeeper.KeeperException;
5 |
6 | import java.io.IOException;
7 | import java.util.concurrent.CountDownLatch;
8 |
9 | public class ZkTest {
10 | private static String lockRootValue = "/lock_zk_test";
11 | private static int sessionTimeoutValue = 10000;
12 | private static String connectionStringValue = "localhost:2181";
13 |
14 | public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
15 |
16 |
17 | final CountDownLatch latch = new CountDownLatch(10);
18 | for (int i = 0; i < 10; i++) {
19 | new Thread(new Runnable() {
20 | public void run() {
21 | DistributedLock lock = null;
22 | try {
23 | lock = new DistributedLock(lockRootValue, connectionStringValue, sessionTimeoutValue);
24 | latch.countDown();
25 | latch.await();
26 | lock.lock();
27 | Thread.sleep(RandomUtils.nextInt(200, 500));
28 | } catch (InterruptedException e) {
29 | e.printStackTrace();
30 | } catch (KeeperException e) {
31 | e.printStackTrace();
32 | } catch (IOException e) {
33 | e.printStackTrace();
34 | } finally {
35 | if (lock != null) {
36 | lock.unlock();
37 | }
38 | }
39 | }
40 | }).start();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lock/distarch-lock-zookeeper/src/main/java/com/liushaoming/lock/zk/ZookeeperClient.java:
--------------------------------------------------------------------------------
1 | package com.liushaoming.lock.zk;
2 |
3 | import org.apache.zookeeper.WatchedEvent;
4 | import org.apache.zookeeper.Watcher;
5 | import org.apache.zookeeper.ZooKeeper;
6 |
7 | import java.io.IOException;
8 | import java.util.concurrent.CountDownLatch;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | /**
12 | *
13 | */
14 | public class ZookeeperClient {
15 |
16 | // private static String connectionString = "appjishu.com:2181";
17 | // private static int sessionTimeout = 10000;
18 |
19 |
20 | // public static ZooKeeper getInstance() throws IOException, InterruptedException {
21 | // //--------------------------------------------------------------
22 | // // 为避免连接还未完成就执行zookeeper的get/create/exists操作引起的(KeeperErrorCode = ConnectionLoss)
23 | // // 这里等Zookeeper的连接完成才返回实例
24 | // //--------------------------------------------------------------
25 | // final CountDownLatch connectedSignal = new CountDownLatch(1);
26 | // ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
27 | // @Override
28 | // public void process(WatchedEvent event) {
29 | // if (event.getState() == Event.KeeperState.SyncConnected) {
30 | // connectedSignal.countDown();
31 | // }
32 | // }
33 | // });
34 | // connectedSignal.await(sessionTimeout, TimeUnit.MILLISECONDS);
35 | // return zk;
36 | // }
37 |
38 | public static ZooKeeper getInstance(String connectionString, int sessionTimeout) throws IOException, InterruptedException {
39 | final CountDownLatch connectedSignal = new CountDownLatch(1);
40 |
41 | ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
42 | @Override
43 | public void process(WatchedEvent event) {
44 | if (event.getState() == Event.KeeperState.SyncConnected) {
45 | connectedSignal.countDown();
46 | }
47 | }
48 | });
49 | connectedSignal.await(sessionTimeout, TimeUnit.MILLISECONDS);
50 | return zk;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | com.appjishu
6 | distarch
7 | 1.0-SNAPSHOT
8 | pom
9 | distarch
10 | distarch
11 |
--------------------------------------------------------------------------------
/spring-boot-starter/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=java
2 | *.css linguist-language=java
3 | *.html linguist-language=java
4 |
--------------------------------------------------------------------------------
/spring-boot-starter/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 | /log/
13 |
14 | ### IntelliJ IDEA ###
15 | .idea
16 | *.iws
17 | *.iml
18 | *.ipr
19 |
20 | ### NetBeans ###
21 | /nbproject/private/
22 | /build/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 |
28 | /config/
29 |
--------------------------------------------------------------------------------
/spring-boot-starter/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/spring-boot-starter/README.md:
--------------------------------------------------------------------------------
1 | # jframe
2 |
3 | 用Java自己去写框架,需要掌握那些技术?
4 | 这个项目中会展示很多核心技术给你。
5 |
6 | - spring.factories
7 |
8 | **自己写spring-boot-starter**
9 |
10 | 测试
11 |
12 | [http://localhost:29001/starter/x123](http://localhost:29001/starter/x123)
13 |
14 | 返回值:
15 |
16 | ```text
17 | pppx123sss
18 | ```
19 | 说明starter运行成功
20 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-app/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 | /log/
13 |
14 | ### IntelliJ IDEA ###
15 | .idea
16 | *.iws
17 | *.iml
18 | *.ipr
19 |
20 | ### NetBeans ###
21 | /nbproject/private/
22 | /build/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 |
28 | /config/
29 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-app/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | jar
8 |
9 |
10 | org.springframework.boot
11 | spring-boot-starter-parent
12 | 2.1.2.RELEASE
13 |
14 |
15 |
16 | com.lsm.jframe
17 | jframe-app
18 | 1.0-SNAPSHOT
19 |
20 |
21 | 1.8
22 | UTF-8
23 | UTF-8
24 | 5.1.4.RELEASE
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-web
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-test
35 | test
36 |
37 |
38 | com.lsm.jframe
39 | jframe-spring-boot-starter
40 | 1.0-SNAPSHOT
41 |
42 |
43 |
44 |
45 |
46 | jframe-app
47 |
48 |
49 | org.springframework.boot
50 | spring-boot-maven-plugin
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-app/src/main/java/com/lsm/jframe/JframeApp.java:
--------------------------------------------------------------------------------
1 | package com.lsm.jframe;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class JframeApp {
8 | public static void main(String[] args){
9 | SpringApplication.run(JframeApp.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-app/src/main/java/com/lsm/jframe/app/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.lsm.jframe.app.controller;
2 |
3 | import com.lsm.jframe.core.api.JframeService;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.web.bind.annotation.PathVariable;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | @RestController
10 | @RequestMapping("")
11 | public class HomeController {
12 |
13 | @Autowired
14 | private JframeService jframeService;
15 |
16 | @RequestMapping("")
17 | public String index() {
18 | return "jframe-app works.";
19 | }
20 |
21 | @RequestMapping("starter/{word}")
22 | public String starter(@PathVariable String word) {
23 | String result = jframeService.wrap(word);
24 | return result;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-app/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 29001
3 |
4 | jframe:
5 | service:
6 | enabled: true
7 | prefix: ppp
8 | suffix: sss
9 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-spring-boot-starter/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 | /log/
13 |
14 | ### IntelliJ IDEA ###
15 | .idea
16 | *.iws
17 | *.iml
18 | *.ipr
19 |
20 | ### NetBeans ###
21 | /nbproject/private/
22 | /build/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 |
28 | /config/
29 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-spring-boot-starter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | com.lsm.jframe
8 | jframe-spring-boot-starter
9 | 1.0-SNAPSHOT
10 |
11 |
12 | UTF-8
13 | 1.8
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-parent
19 | 2.1.2.RELEASE
20 |
21 |
22 |
23 |
25 |
26 | org.springframework.boot
27 | spring-boot-configuration-processor
28 | true
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-autoconfigure
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-spring-boot-starter/src/main/java/com/lsm/jframe/core/api/JframeService.java:
--------------------------------------------------------------------------------
1 | package com.lsm.jframe.core.api;
2 |
3 | public class JframeService {
4 | private String prefix;
5 | private String suffix;
6 |
7 | public JframeService(String prefix, String suffix) {
8 | this.prefix = prefix;
9 | this.suffix = suffix;
10 | }
11 | public String wrap(String word) {
12 | return prefix + word + suffix;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-spring-boot-starter/src/main/java/com/lsm/jframe/core/config/JframeAutoConfigure.java:
--------------------------------------------------------------------------------
1 | package com.lsm.jframe.core.config;
2 |
3 | import com.lsm.jframe.core.api.JframeService;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
8 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 |
12 | @Configuration
13 | @ConditionalOnClass(JframeService.class)
14 | @EnableConfigurationProperties(JframeServiceProperties.class)
15 | public class JframeAutoConfigure {
16 |
17 | private final JframeServiceProperties properties;
18 |
19 | @Autowired
20 | public JframeAutoConfigure(JframeServiceProperties properties) {
21 | this.properties = properties;
22 | }
23 |
24 | @Bean
25 | @ConditionalOnMissingBean
26 | @ConditionalOnProperty(prefix = "jframe.service", value = "enabled",havingValue = "true")
27 | JframeService jframeService (){
28 | return new JframeService(properties.getPrefix(), properties.getSuffix());
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-spring-boot-starter/src/main/java/com/lsm/jframe/core/config/JframeServiceProperties.java:
--------------------------------------------------------------------------------
1 | package com.lsm.jframe.core.config;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 |
5 | @ConfigurationProperties("jframe.service")
6 | public class JframeServiceProperties {
7 | private String prefix;
8 | private String suffix;
9 |
10 | public String getPrefix() {
11 | return prefix;
12 | }
13 |
14 | public void setPrefix(String prefix) {
15 | this.prefix = prefix;
16 | }
17 |
18 | public String getSuffix() {
19 | return suffix;
20 | }
21 |
22 | public void setSuffix(String suffix) {
23 | this.suffix = suffix;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/spring-boot-starter/jframe-spring-boot-starter/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | com.lsm.jframe.core.config.JframeAutoConfigure
--------------------------------------------------------------------------------
/spring-boot-starter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 | pom
7 |
8 | com.lsm.jframe
9 | jframe
10 | 1.0-SNAPSHOT
11 |
12 |
13 | jframe-spring-boot-starter
14 | jframe-app
15 |
16 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/.gitignore:
--------------------------------------------------------------------------------
1 | # maven ignore
2 | target/
3 | *.jar
4 | *.war
5 | *.zip
6 | *.tar
7 |
8 | # eclipse ignore
9 | .settings/
10 | .project
11 | .classpath
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | logs/
21 | *.doc
22 | *.log
23 | *.cache
24 | *.diff
25 | *.patch
26 | *.tmp
27 |
28 | # system ignore
29 | .DS_Store
30 | Thumbs.db
31 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/README.md:
--------------------------------------------------------------------------------
1 | # sword-springboot
2 |
3 | com.appjishu.swordboot.boot.AppContextHolder
4 |
5 |
6 | 应用场景:
7 | 一般情况下,使用SpringMVC/SpringBoot的时候,各种bean注册到Spring容器里了,然后在需要这个bean的地方,
8 | 使用@Autowired
或者@Resource
标注的bean都可以被自动注入。 但是在某些场景下,
9 | 需要手动注入。比如在一个Util里面,这个Util里面的方法都是static的,这个时候,如果需要获取Spring容器中的某个
10 | bean,或者获取到ApplicationContext, 这个时候,就需要一个ApplicationContext Holder的东西,这里命名为AppContextHolder
11 |
12 | 其实Spring里有一个接口就是为了这个应用场景而生的ApplicationContextAware
13 | 本人就开发了一个com.appjishu.swordboot.boot.AppContextHolder
实现了这个接口, 源码如下:
14 | ```java
15 | package com.appjishu.swordboot.boot;
16 |
17 | import org.springframework.beans.BeansException;
18 | import org.springframework.context.ApplicationContext;
19 | import org.springframework.context.ApplicationContextAware;
20 | import org.springframework.stereotype.Component;
21 |
22 | /**
23 | *
24 | * @author liushaoming
25 | *
26 | */
27 | @Component
28 | public class AppContextHolder implements ApplicationContextAware {
29 | private static ApplicationContext applicationContext = null;
30 |
31 | @Override
32 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
33 | if (AppContextHolder.applicationContext == null) {
34 | AppContextHolder.applicationContext = applicationContext;
35 | }
36 | }
37 |
38 | // 获取applicationContext
39 | public static ApplicationContext getApplicationContext() {
40 | return applicationContext;
41 | }
42 |
43 | // 通过name获取 Bean.
44 | public static Object getBean(String name) {
45 | return getApplicationContext().getBean(name);
46 | }
47 |
48 | // 通过class获取Bean.
49 | public static T getBean(Class clazz) {
50 | return getApplicationContext().getBean(clazz);
51 | }
52 |
53 | // 通过name,以及Clazz返回指定的Bean
54 | public static T getBean(String name, Class clazz) {
55 | return getApplicationContext().getBean(name, clazz);
56 | }
57 | }
58 | ```
59 | 使用的时候,很简单, 就像使用Spring原生的ApplicationContext一样, 现在要获取一个已经注册到容器里的一个bean Student
60 | ```java
61 | @Configuration
62 | public class SysConfig {
63 | @Bean(name = "myStudent")
64 | public Student student() {
65 | Student student=new Student();
66 | student.setName("Frank Liu");
67 | student.setAddress("Shanghai");
68 | return student;
69 | }
70 | }
71 | ```
72 | 使用AppContextHolder获取bean的操作
73 | ```java
74 | Student student0 = (Student) AppContextHolder.getBean("myStudent");
75 | // 或者
76 | Student student1 = (Student) AppContextHolder.getBean(Student.class);
77 | log.info("student0.name={}", student0.getName());
78 | ```
79 | 测试方法:
80 | 在idea/eclipse里,导入maven项目sword-springboot, 右键运行SwordBootApp -> run as -> Java Application
81 | 这是一个springboot项目,打开浏览器访问地址[http://localhost:15000/test](http://localhost:15000/test)
82 | 可以运行测试AppContextHolder的测试代码, 贴出TestUtil里的方法
83 | ```java
84 | public class TestUtil {
85 | private static final Logger log = LoggerFactory.getLogger(TestUtil.class);
86 |
87 | public static void testAppContextHolder() {
88 | log.info("--testAppContextHolder start---");
89 | Student student0 = (Student) AppContextHolder.getBean("myStudent");
90 | Student student1 = (Student) AppContextHolder.getBean(Student.class);
91 |
92 | // 如果"student0 == student1"成立,则验证了AppContextHolder的确能获取到Spring容器ApplicationContext
93 | // 而且Spring容器默认注册bean使用的是单例模式
94 | log.info("student0==student1? {}", student0 == student1);
95 | log.info("-------------------");
96 | log.info("student0.name={}", student0.getName());
97 | log.info("student0.address={}", student0.getAddress());
98 | log.info("-------------------");
99 | log.info("student1.name={}", student1.getName());
100 | log.info("student1.address={}", student1.getAddress());
101 | log.info("--testAppContextHolder done---");
102 | }
103 | }
104 | ```
105 | 打印结果: 说明测试成功了
106 | ```
107 | 2019-01-06 15:43:47.563 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : --testAppContextHolder start---
108 | 2019-01-06 15:43:47.564 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0==student1? true
109 | 2019-01-06 15:43:47.564 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : -------------------
110 | 2019-01-06 15:43:47.565 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0.name=Frank Liu
111 | 2019-01-06 15:43:47.565 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0.address=Shanghai
112 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : -------------------
113 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student1.name=Frank Liu
114 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student1.address=Shanghai
115 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : --testAppContextHolder done---
116 | ```
117 |
118 | 注意事项:
119 | AppContextHolder在SpringBoot里使用,只需要在类名AppContextHolder
前标注@Component
120 | 如果在SpringMVC里使用的,需要用@Component标注类AppContextHolder, 并且在applicationContext.xml里面设置
121 | 包含AppContextHolder所在的package, 或者直接用bean
标签来注册它。
122 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.appjishu
8 | distarch-springboot
9 | 1.0-SNAPSHOT
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.1.0.RELEASE
15 |
16 |
17 |
18 |
19 | UTF-8
20 | UTF-8
21 | 1.8
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-web
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-test
32 | test
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/java/com/appjishu/SwordBootApp.java:
--------------------------------------------------------------------------------
1 | package com.appjishu;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class SwordBootApp {
8 | public static void main(String[] args){
9 | SpringApplication.run(SwordBootApp.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/java/com/appjishu/swordboot/boot/AppContextHolder.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.swordboot.boot;
2 |
3 | import org.springframework.beans.BeansException;
4 | import org.springframework.context.ApplicationContext;
5 | import org.springframework.context.ApplicationContextAware;
6 | import org.springframework.stereotype.Component;
7 |
8 | /**
9 | *
10 | * @author liushaoming
11 | *
12 | */
13 | @Component
14 | public class AppContextHolder implements ApplicationContextAware {
15 | private static ApplicationContext applicationContext = null;
16 |
17 | @Override
18 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
19 | if (AppContextHolder.applicationContext == null) {
20 | AppContextHolder.applicationContext = applicationContext;
21 | }
22 | }
23 |
24 | // 获取applicationContext
25 | public static ApplicationContext getApplicationContext() {
26 | return applicationContext;
27 | }
28 |
29 | // 通过name获取 Bean.
30 | public static Object getBean(String name) {
31 | return getApplicationContext().getBean(name);
32 | }
33 |
34 | // 通过class获取Bean.
35 | public static T getBean(Class clazz) {
36 | return getApplicationContext().getBean(clazz);
37 | }
38 |
39 | // 通过name,以及Clazz返回指定的Bean
40 | public static T getBean(String name, Class clazz) {
41 | return getApplicationContext().getBean(name, clazz);
42 | }
43 | }
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/java/com/appjishu/swordboot/boot/SysConfig.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.swordboot.boot;
2 |
3 | import com.appjishu.swordboot.model.Student;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | @Configuration
8 | public class SysConfig {
9 | @Bean(name = "myStudent")
10 | public Student student() {
11 | Student student=new Student();
12 | student.setName("Frank Liu");
13 | student.setAddress("Shanghai");
14 | return student;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/java/com/appjishu/swordboot/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.swordboot.controller;
2 |
3 | import com.appjishu.swordboot.util.TestUtil;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | @RequestMapping("")
8 | @RestController
9 | public class HomeController {
10 | @RequestMapping("")
11 | public String index() {
12 | return "Access sword-springboot OK.";
13 | }
14 |
15 | @RequestMapping("/test")
16 | public String test() {
17 | TestUtil.testAppContextHolder();
18 | return "Test done.";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/java/com/appjishu/swordboot/model/Student.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.swordboot.model;
2 |
3 | public class Student {
4 | private String name;
5 | private String address;
6 |
7 | public String getName() {
8 | return name;
9 | }
10 |
11 | public void setName(String name) {
12 | this.name = name;
13 | }
14 |
15 | public String getAddress() {
16 | return address;
17 | }
18 |
19 | public void setAddress(String address) {
20 | this.address = address;
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return "Student{" +
26 | "name='" + name + '\'' +
27 | ", address='" + address + '\'' +
28 | '}';
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/java/com/appjishu/swordboot/util/TestUtil.java:
--------------------------------------------------------------------------------
1 | package com.appjishu.swordboot.util;
2 |
3 | import com.appjishu.swordboot.boot.AppContextHolder;
4 | import com.appjishu.swordboot.model.Student;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | public class TestUtil {
9 | private static final Logger log = LoggerFactory.getLogger(TestUtil.class);
10 |
11 | public static void testAppContextHolder() {
12 | log.info("--testAppContextHolder start---");
13 | Student student0 = (Student) AppContextHolder.getBean("myStudent");
14 | Student student1 = (Student) AppContextHolder.getBean(Student.class);
15 |
16 | // 如果"student0 == student1"成立,则验证了AppContextHolder的确能获取到Spring容器ApplicationContext
17 | // 而且Spring容器默认注册bean使用的是单例模式
18 | log.info("student0==student1? {}", student0 == student1);
19 | log.info("-------------------");
20 | log.info("student0.name={}", student0.getName());
21 | log.info("student0.address={}", student0.getAddress());
22 | log.info("-------------------");
23 | log.info("student1.name={}", student1.getName());
24 | log.info("student1.address={}", student1.getAddress());
25 | log.info("--testAppContextHolder done---");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/spring/distarch-springboot/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=15000
2 |
--------------------------------------------------------------------------------