0%

文章字数:794,阅读全文大约需要3分钟

OSHI可以跨平台查看服务器信息,其中cpu负载信息为当前占用CPU的时间。需要在一段时间内获取两次,然后相减得出这段时间内所占用的时间。这段时间除以总占用时间就是占用百分比。

环境java1.8

使用

  1. 依赖
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>3.12.2</version>
    </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package io.greatcolin.jvmMessage;

import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;

import java.text.DecimalFormat;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
* @author colin.cheng
* @version V1.0
* @date Created In 16:04 2019/8/16
*/
public class OshiTest {

public static void main(String[] args) {
while (true){
try {
OshiTest.printlnCpuInfo();
OshiTest.MemInfo();
OshiTest.getThread();
OshiTest.setSysInfo();
OshiTest.setJvmInfo();
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
}
}

private static void printlnCpuInfo() throws InterruptedException {
//System.out.println("----------------cpu信息----------------");
SystemInfo systemInfo = new SystemInfo();
CentralProcessor processor = systemInfo.getHardware().getProcessor();
long[] prevTicks = processor.getSystemCpuLoadTicks();
// 睡眠1s
TimeUnit.SECONDS.sleep(1);
long[] ticks = processor.getSystemCpuLoadTicks();
long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
System.out.println("----------------cpu信息----------------");
System.out.println("cpu核数:" + processor.getLogicalProcessorCount());
System.out.println("cpu系统使用率:" + new DecimalFormat("#.##%").format(cSys * 1.0 / totalCpu));
System.out.println("cpu用户使用率:" + new DecimalFormat("#.##%").format(user * 1.0 / totalCpu));
System.out.println("cpu当前等待率:" + new DecimalFormat("#.##%").format(iowait * 1.0 / totalCpu));
System.out.println("cpu当前使用率:" + new DecimalFormat("#.##%").format(1.0-(idle * 1.0 / totalCpu)));
}

public static void MemInfo(){
System.out.println("----------------主机内存信息----------------");
SystemInfo systemInfo = new SystemInfo();
GlobalMemory memory = systemInfo.getHardware().getMemory();
//总内存
long totalByte = memory.getTotal();
//剩余
long acaliableByte = memory.getAvailable();
System.out.println("总内存 = " + formatByte(totalByte));
System.out.println("使用" + formatByte(totalByte-acaliableByte));
System.out.println("剩余内存 = " + formatByte(acaliableByte));
System.out.println("使用率:" + new DecimalFormat("#.##%").format((totalByte-acaliableByte)*1.0/totalByte));
}

public static void setSysInfo(){
System.out.println("----------------操作系统信息----------------");
Properties props = System.getProperties();
//系统名称
String osName = props.getProperty("os.name");
//架构名称
String osArch = props.getProperty("os.arch");
System.out.println("操作系统名 = " + osName);
System.out.println("系统架构 = " + osArch);
}

public static void setJvmInfo(){
System.out.println("----------------jvm信息----------------");
Properties props = System.getProperties();
Runtime runtime = Runtime.getRuntime();
//jvm总内存
long jvmTotalMemoryByte = runtime.totalMemory();
//jvm最大可申请
long jvmMaxMoryByte = runtime.maxMemory();
//空闲空间
long freeMemoryByte = runtime.freeMemory();
//jdk版本
String jdkVersion = props.getProperty("java.version");
//jdk路径
String jdkHome = props.getProperty("java.home");
System.out.println("jvm内存总量 = " + formatByte(jvmTotalMemoryByte));
System.out.println("jvm已使用内存 = " + formatByte(jvmTotalMemoryByte-freeMemoryByte));
System.out.println("jvm剩余内存 = " + formatByte(freeMemoryByte));
System.out.println("jvm内存使用率 = " + new DecimalFormat("#.##%").format((jvmTotalMemoryByte-freeMemoryByte)*1.0/jvmTotalMemoryByte));
System.out.println("java版本 = " + jdkVersion);
//System.out.println("jdkHome = " + jdkHome);
}

public static void getThread(){
System.out.println("----------------线程信息----------------");
ThreadGroup currentGroup =Thread.currentThread().getThreadGroup();

while (currentGroup.getParent()!=null){
// 返回此线程组的父线程组
currentGroup=currentGroup.getParent();
}
//此线程组中活动线程的估计数
int noThreads = currentGroup.activeCount();

Thread[] lstThreads = new Thread[noThreads];
//把对此线程组中的所有活动子组的引用复制到指定数组中。
currentGroup.enumerate(lstThreads);
for (Thread thread : lstThreads) {
System.out.println("线程数量:"+noThreads+" 线程id:" + thread.getId() + " 线程名称:" + thread.getName() + " 线程状态:" + thread.getState());
}
}

public static String formatByte(long byteNumber){
//换算单位
double FORMAT = 1024.0;
double kbNumber = byteNumber/FORMAT;
if(kbNumber<FORMAT){
return new DecimalFormat("#.##KB").format(kbNumber);
}
double mbNumber = kbNumber/FORMAT;
if(mbNumber<FORMAT){
return new DecimalFormat("#.##MB").format(mbNumber);
}
double gbNumber = mbNumber/FORMAT;
if(gbNumber<FORMAT){
return new DecimalFormat("#.##GB").format(gbNumber);
}
double tbNumber = gbNumber/FORMAT;
return new DecimalFormat("#.##TB").format(tbNumber);
}
}

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 没添加slf4j的依赖,不影响
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
----------------cpu信息----------------
cpu核数:4
cpu系统使用率:1.88%
cpu用户使用率:2.73%
cpu当前等待率:0%
cpu当前使用率:4.71%
----------------主机内存信息----------------
总内存 = 7.88GB
使用5.89GB
剩余内存 = 1.99GB
使用率:74.72%
----------------线程信息----------------
线程数量:5 线程id:2 线程名称:Reference Handler 线程状态:WAITING
线程数量:5 线程id:3 线程名称:Finalizer 线程状态:WAITING
线程数量:5 线程id:4 线程名称:Signal Dispatcher 线程状态:RUNNABLE
线程数量:5 线程id:5 线程名称:Attach Listener 线程状态:RUNNABLE
线程数量:5 线程id:1 线程名称:main 线程状态:RUNNABLE
----------------操作系统信息----------------
操作系统名 = Windows 7
系统架构 = amd64
----------------jvm信息----------------
jvm内存总量 = 123MB
jvm已使用内存 = 20.46MB
jvm剩余内存 = 102.54MB
jvm内存使用率 = 16.64%
java版本 = 1.8.0_65

文章字数:777,阅读全文大约需要3分钟

封装了一个小工具

环境

  1. jdk1.8

  2. maven依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>

思路

  1. 私钥和公钥这两个对象都有加密和解密的动作,于是定义一个接口 CryptographicProcessor。私钥和公钥都继承这个接口,并实现相应加密和解密动作。

  2. 工具类SecurityUtils有三个功能: 随机生成秘钥对、获取私钥处理类、获取公钥处理类。

  3. 具体流程就是:

1
2
3
1. 使用SecurityUtils的静态方法生成一个Map,里面是私钥加公钥。
2. 使用SecurityUtils的静态方法传入私钥获取私钥处理器,这个处理器可以使用私钥加密和解密。
3. 把密文和公钥发给其他人,再使用公钥处理器解密,使用公钥加密返回信息,再用私钥解密返回的信息。
  1. 无论是秘钥还是明文密文,都是byte[]类型。为了方便base64转换成了String,传入值是String的都是会自动Base64

代码

操作接口

公钥和秘钥处理器都有加密解密的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @author colin.cheng
* @date
*/
public interface CryptographicProcessor {
/**
* 加密
*
* @param str
* 原信息
* @return 密文
* @throws Exception
*/
String encode(String str) throws Exception;

byte[] encode(byte[] strs) throws Exception;

/**
* 解密
*
* @param str
* 密文
* @return 原信息
* @throws Exception
*/
String decode(String str) throws Exception;

byte[] decode(byte[] strs) throws Exception;
}

##工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;

import org.apache.commons.codec.binary.Base64;

/**
* @author colin.cheng
* @date
*/
public class SecurityUtils {

/**
* 随机生成的秘钥Map中公钥的key
*/
static final String RSA_KEY_PUBLIC = "public";

/**
* 随机生成的秘钥Map中私钥的key
*/
static final String RSA_KEY_PRIVATE = "private";

private SecurityUtils() {}

/**
* 获取公钥处理器
*
* @param rSAPublicKey
* base64公钥
* @return 处理器对象
* @throws Exception
*/
public static CryptographicProcessor getRSAPublicKeyProcessor(String rSAPublicKey) throws Exception {
return new RSAPublicKeyProcessor(rSAPublicKey);
}

/**
* 获取公钥处理器
*
* @param rSAPublicKeyBytes
* 公钥byte数组
* @return 处理器对象
* @throws Exception
*/
public static CryptographicProcessor getRSAPublicKeyProcessor(byte[] rSAPublicKeyBytes) throws Exception {
return new RSAPublicKeyProcessor(rSAPublicKeyBytes);
}

/**
* 获取私钥处理器
*
* @param rSAPrivateKey
* base64私钥
* @return 处理器对象
* @throws Exception
*/
public static CryptographicProcessor getRSAPrivateKeyProcessor(String rSAPrivateKey) throws Exception {
return new RSAPrivateKeyProcessor(rSAPrivateKey);
}

/**
* 获取私钥处理器
*
* @param rSAPrivateKeyBytes
* 私钥byte数组
* @return 处理器对象
* @throws Exception
*/
public static CryptographicProcessor getRSAPrivateKeyProcessor(byte[] rSAPrivateKeyBytes) throws Exception {
return new RSAPrivateKeyProcessor(rSAPrivateKeyBytes);
}

/**
* 生成生成base64秘钥秘钥 明文最大长度不能大于秘钥长度 默认大小512byte
*
* @return
* @throws NoSuchAlgorithmException
*/
public static Map<String, String> generateRSAKeyPair() throws NoSuchAlgorithmException {
return generateRSAKeyPair(512);
}

/**
* 生成base64秘钥 明文最大长度不能大于秘钥长度
*
* @param keySize
* 秘钥大小
* @return
* @throws NoSuchAlgorithmException
*/
public static Map<String, String> generateRSAKeyPair(int keySize) throws NoSuchAlgorithmException {
Map<String, String> keyPairMap = new HashMap<>();
KeyPair keyPair = getKeyPair(keySize);
keyPairMap.put(SecurityUtils.RSA_KEY_PUBLIC, Base64.encodeBase64String(keyPair.getPrivate().getEncoded()));
keyPairMap.put(SecurityUtils.RSA_KEY_PRIVATE, Base64.encodeBase64String(keyPair.getPrivate().getEncoded()));
return keyPairMap;
}

/**
* 生成秘钥 明文最大长度不能大于秘钥长度
*
* @param keySize
* 秘钥大小
* @return
* @throws NoSuchAlgorithmException
*/
public static Map<String, byte[]> generateRSAKeyBytePair(int keySize) throws NoSuchAlgorithmException {
Map<String, byte[]> keyPairMap = new HashMap<>();
KeyPair keyPair = getKeyPair(keySize);
keyPairMap.put(SecurityUtils.RSA_KEY_PUBLIC, keyPair.getPublic().getEncoded());
keyPairMap.put(SecurityUtils.RSA_KEY_PRIVATE, keyPair.getPrivate().getEncoded());
return keyPairMap;
}

private static KeyPair getKeyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(keySize);
return keyPairGenerator.generateKeyPair();
}

// -------------class----------------

static class RSAPublicKeyProcessor implements CryptographicProcessor {

private X509EncodedKeySpec x509EncodedKeySpec;
private KeyFactory keyFactory;
private PublicKey publicKey;
private Cipher cipher;

private RSAPublicKeyProcessor(String rSAPublicKey) throws Exception {
this(Base64.decodeBase64(rSAPublicKey));
}

private RSAPublicKeyProcessor(byte[] rSAPublicKeyBytes) throws Exception {
this.x509EncodedKeySpec = new X509EncodedKeySpec(rSAPublicKeyBytes);
this.keyFactory = KeyFactory.getInstance("RSA");
this.cipher = Cipher.getInstance("RSA");
this.publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
}

@Override
public String encode(String str) throws Exception {
byte[] result = encode(str.getBytes());
return Base64.encodeBase64String(result);
}

@Override
public byte[] encode(byte[] strs) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(strs);
}

@Override
public String decode(String str) throws Exception {
byte[] result = decode(Base64.decodeBase64(str));
return new String(result);
}

@Override
public byte[] decode(byte[] strs) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(strs);
}
}

static class RSAPrivateKeyProcessor implements CryptographicProcessor {

private PKCS8EncodedKeySpec pkcs8EncodedKeySpec;
private KeyFactory keyFactory;
private PrivateKey privateKey;
private Cipher cipher;

private RSAPrivateKeyProcessor(String rSAPrivate)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
this(Base64.decodeBase64(rSAPrivate));
}

private RSAPrivateKeyProcessor(byte[] rSAPrivateBytes)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
this.pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rSAPrivateBytes);
this.keyFactory = KeyFactory.getInstance("RSA");
this.privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
this.cipher = Cipher.getInstance("RSA");
}

@Override
public byte[] encode(byte[] strs) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(strs);
return result;
}

@Override
public String encode(String str) throws Exception {
byte[] result = encode(str.getBytes());
return Base64.encodeBase64String(result);
}

@Override
public byte[] decode(byte[] strs) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(strs);
return result;
}

@Override
public String decode(String str) throws Exception {
byte[] result = decode(Base64.decodeBase64(str));
return new String(result);
}
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* @author colin.cheng
* @date
*/
import java.util.Map;

public class security {

public static void main(String[] args) {
try {
String mess = "哈哈哈";
//秘钥对
Map<String, byte[]> keyPair = SecurityUtils.generateRSAKeyBytePair(512);
//私钥处理器
CryptographicProcessor privateProcessor =
SecurityUtils.getRSAPrivateKeyProcessor(keyPair.get(SecurityUtils.RSA_KEY_PRIVATE));
//加密的到密文
String cipherText = privateProcessor.encode(mess);
//假装发给别人
String res = sendMess(cipherText, keyPair.get(SecurityUtils.RSA_KEY_PUBLIC));
//私钥解密公钥加密的密文
System.out.println("客户端收到回复:"+privateProcessor.decode(res));
} catch (Exception e) {
// 创建失败
e.printStackTrace();
}
}

public static String sendMess(String mess, byte[] publicKey) throws Exception {
//根据拿到的公钥生成公钥处理器,网络传输可以生成string类型的秘钥。
CryptographicProcessor publicProce = SecurityUtils.getRSAPublicKeyProcessor(publicKey);
//解密
System.out.println("服务端收到:" + publicProce.decode(mess));
//加密一个返回值
return publicProce.encode("200");
}
}

结果

1
2
服务端收到:哈哈哈
客户端收到回复:200

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

RestTemplate是spring提供的访问Rest服务的客户端,默认使用jdk的http连接工具(HttpURLConnection)。可以通过setRequestFactory属性切换到其它http源, Apache HttpComponentsNettyOkHttp等。

一、入口

入口方法主要根据HTP的六个方法制定的:

HTTP method RestTempalte methods
GET getForObject
- getForENtity
POST poetForLocation
- postForObject
DELETE delete
PUT put
HEAD headForHeaders
OPTIONS optionsForAllow
通用方法 exchange
- execute

注:默认使用HttpMessageConverter实例完成POJPHTTP的转换,也可以通过setMessageConverters注册其它的转换器。(@ResponseBody使用的也是HttpMessageConverter

二、GET

getForObject()getForEntity() 解析类写String.class获取的就是字符串形式的数据

getForObject()方法

1
2
3
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
public <T> T getForObject(URI url, Class<T> responseType)

注:getForObject()比getForEntity()多了HTTP转换成POJO,但是省略了response信息。

getForObject()方法实例

1
2
3
4
5
6
7
8
9
10
11
12
13
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
//无参
MessObj messObj1 = restTemplate.getForObject("http://xxx.com/test",MessObj .class);
//占位符传参
MessObj messObj2 = restTemplate.getForObject("http://xxx.com/test/{1}/{2}",MessObj .class,"参数1","参数2");
//map传参
Map<String,String> map = new HashMap();
map.put("id","1");
map.put("name","zs")
MessObj messObj1 = restTemplate.getForObject("http://xxx.com/test",MessObj .class,map);
//结果:messObj{status=200,data=[{a=1}...]...}
}

getForEntity()方法

1
2
3
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}

注:返回的是ResponseEntity对象,需要json工具解析成pojoResponseEntityHttpStatus getStatusCode()BodyBuildercreated(URI location)等方法,方便查看更多信息。

ResponseEntity.java

1
2
3
4
5
6
7
8
public HttpStatus getStatusCode(){}
public int getStatusCodeValue(){}
public boolean equals(@Nullable Object other) {}
public String toString() {}
public static BodyBuilder status(HttpStatus status) {}
public static BodyBuilder ok() {}
public static <T> ResponseEntity<T> ok(T body) {}
public static BodyBuilder created(URI location) {}

HttpStatus.java

1
2
3
4
5
6
7
8
public enum HttpStatus {
public boolean is1xxInformational() {}
public boolean is2xxSuccessful() {}
public boolean is3xxRedirection() {}
public boolean is4xxClientError() {}
public boolean is5xxServerError() {}
public boolean isError() {}
}

BodyBuilder.java

1
2
3
4
5
6
7
8
public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
//设置正文的长度,以字节为单位,由Content-Length标头
BodyBuilder contentLength(long contentLength);
//设置body的MediaType 类型
BodyBuilder contentType(MediaType contentType);
//设置响应实体(ResponseEntity)的主体(内容)并返回它。
<T> ResponseEntity<T> body(@Nullable T body);

getForEntity()方法实例:

1
2
3
4
5
6
7
public void gettTest(){
RestTemplate resTemplate = new RestTemplate();
ResponseEntity<MessObj> entity = restTemplate.getForEntity("http://xx.com/test",MessObj.class);
HttpStatus hsc = entity.getStatusCode();
MessObj msObj = entity.getBody();
ResponseEntity.BodyBuilder status = ResponseEntity.status(hsc);
}

三、POST

postForObject()postForEntity()

postForObject方法

1
2
3
4
5
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {}
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {}
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {}

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void postTest(){
RestTemplate restTemplate = new RestTemplate();
//第一个参数url
String url = "http://xxx.com/test";
//第二个参数request(val,header)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType);
//一个key对应多个val的map
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("name", "zhangsan");
//request
HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(map,headers);
//执行 url,http主
ResponseEntity<String> response = restTemplate.postForEntity(url,request,String.class);
//返回值{"status":500,"msg":"xxx","data":null}
System.out.println(response.getBody());
}

头类型
MediaType

1
2
3
4
5
json : application/json
xml : application/xml
png : image/png
jpg : image/jpeg
gif : imge/gif

excute()和exchange()

excute()返回映射对象<T>,exchange()返回ResponseEntity<T>

1
2
3
4
5
6
7
restTemplate.exchange(
String url, //地址
HttpMethod method,//请求类型(HttpMethod.POST/PUT/DELETE/GET)
HttpEntity requestEntity, //请求主体:请求体、头、内容
Class responseType, //返回实体类
Object uriVariables[]//url参数
)

设置超时

1
2
3
4
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(5 * 1000);
clientHttpRequestFactory.setReadTimeout(5 * 1000);
RestTemplate rest = new RestTemplate(clientHttpRequestFactory);

封装工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 获取request
*
* @param params
* @return
*/
private HttpEntity<MultiValueMap<Object, Object>> getRequestByParam(Object[]... params) {
final HttpHeaders headers = new HttpHeaders();
final LinkedMultiValueMap<Object, Object> map = new LinkedMultiValueMap<>();
if (params != null) {
for (Object[] param : params) {
map.add(param[0], param[1]);
}
}
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
return new HttpEntity<>(map, headers);
}

/**
* 通过url发送post请求
*
* @param url
* @param param
* @return
*/
private String postFromUrl(String url, Object[]... param) {
final HttpEntity<MultiValueMap<Object, Object>> request = getRequestByParam(param);
final ResponseEntity<String> entity = restTemplate.postForEntity(url, request, String.class);
if (entity.getStatusCode().is2xxSuccessful()) {
return entity.getBody();
}
throw new RuntimeException("conn error");
}


/**
* 通过url发送get请求
*
* @param url
* @return
*/
private String getFromUrl(String url) {
final ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
if (entity.getStatusCode().is2xxSuccessful()) {
return entity.getBody();
}
throw new RuntimeException("conn error");
}
  • 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
File image = new File("f://xx.jpg");
FileSystemResource file = new FileSystemResource(image);
// 上传信息及文件
String res = postFromUrl("http://xx.xx.xx",
new Object[] {"name", "xx.jpg"}, new Object[] {"image", file});

// 使用byte[]上传
final ByteArrayResource file = new ByteArrayResource(image) {
@Override
public String getFilename() {
return "xx.jpg";
}
};
String res = postFromUrl("http://xx.xx.xx",
new Object[] {"name", "xx.jpg"}, new Object[] {"image", file});

文章字数:784,阅读全文大约需要3分钟

SpringBoot内置了Servlet容器,所以可以直接运行。而传统的javaWeb程序需要嵌入到Tomcat之类的Servlet容器中才能运行。方便之余却带了问题,我们不能像传统的javaWeb程序那样操作web.xml。所以Spring提供了自定义容器内容的途径。

自动配置

自动配置类EmbeddedServletContainerAutoConfiguration

首先看springBoot如何自动配置的。
spring-boot-autoconfigure-xxx.jarweb模块中可以找到

EmbeddedServletContainerAutoConfiguration

1
2
3
4
5
6
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
...

这个类中主要作用是配置三个容器工厂的bean
都是EmbeddedServletContainerFactory接口的

  1. TomcatEmbeddedServletContainerFactory
  2. JettyEmbeddedServletContainerFactory
  3. UndertowEmbeddedServletContainerFactory

通过注解设置有servlet依赖和类对应的servlet容器依赖时,没有其它EmbeddedServletContainerFactory接口时创建。(重点1)

容器工厂接口EmbeddedServletContainerFactory

EmbeddedServletContainerFactory接口内部只有获取嵌入式servlet容器的方法

1
2
3
4
5
public interface EmbeddedServletContainerFactory {

EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers);
}

容器接口EmbeddedServletContainer

EmbeddedServletContainerFactory获取的容器就是EmbeddedServletContainer返回的类型。

也对应三个实现类

  1. TomcatEmbeddedServletContainer
  2. JettyEmbeddedServletContainer
  3. UndertowEmbeddedServletContainer

Tomcat容器工厂TomcatEmbeddedServletContainerFactory类为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
//创建一个Tomcat
Tomcat tomcat = new Tomcat();

//配置Tomcat的基本环节
File baseDir = (this.baseDirectory != null this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 绑定端口的连接
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
// 是否设置Tomcat自动部署
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);

//包装tomcat对象,返回一个嵌入式Tomcat容器,内部会启动该tomcat容器
return getTomcatEmbeddedServletContainer(tomcat);
}

最后调用的方法,主要就是创建Tomcat容器,启动容器。

1
2
3
4
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}

自定义容器参数

EmbeddedServletContainerCustomizer定制器

自定义属性可以通过ServerPropertiesEmbeddedServletContainerCustomizer定制器实现,ServerPropertiesEmbeddedServletContainerCustomizer的子类,所以其实都是EmbeddedServletContainerCustomizer在起作用。

BeanPostProcessorsRegistrar给容器导入组件的类

在最顶级的自动配置类EmbeddedServletContainerAutoConfiguration上有个注解@Import(BeanPostProcessorsRegistrar.class)
导入了BeanPostProcessorsRegistrar这个类成Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static class BeanPostProcessorsRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

private ConfigurableListableBeanFactory beanFactory;

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}

// 重点方法,导入了配置器
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
//注册了一个EmbeddedServletContainerCustomizerBeanPostProcessor后置处理器的Bean
registerSyntheticBeanIfMissing(registry,
"embeddedServletContainerCustomizerBeanPostProcessor",
EmbeddedServletContainerCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry,
"errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}

private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
String name, Class beanClass) {
if (ObjectUtils.isEmpty(
this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
  1. 后置处理器:在bean初始化前(创建完成,还未属性赋值),会执行初始化工作。

  2. registerBeanDefinitions这个方法导入了EmbeddedServletContainerCustomizerBeanPostProcessorBean

  3. 这个Bean会从ICO容器中拿到所有EmbeddedServletContainerCustomizer类,也就是上面说的定制器。可以引入这个定制器来操作容器参数内容

通过容器工厂自定义参数

EmbeddedServletContainerFactory容器工厂

也就是上面的重点1,spring会根据依赖和有没有其他的工厂来判断是否要注入。我们可以自定义工厂,并加入Bean

例:

  1. 禁用post和get之外的方法
    配置类中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    @Bean  
    public EmbeddedServletContainerFactory servletContainer() {
    TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {// 1
    protected void postProcessContext(Context context) {
    SecurityConstraint securityConstraint = new SecurityConstraint();
    securityConstraint.setUserConstraint("CONFIDENTIAL");
    SecurityCollection collection = new SecurityCollection();
    // 对于所有的请求
    collection.addPattern("/*");
    // 都验证一下http方法(servlet自带的权限验证,因为没有定义用户,所有直接拦截)
    collection.addMethod("HEAD");
    collection.addMethod("PUT");
    collection.addMethod("DELETE");
    collection.addMethod("OPTIONS");
    collection.addMethod("TRACE");
    collection.addMethod("COPY");
    collection.addMethod("SEARCH");
    collection.addMethod("PROPFIND");
    securityConstraint.addCollection(collection);
    context.addConstraint(securityConstraint);
    }
    };
    //如果需要禁用TRACE请求,需添加以下代码:
    tomcat.addConnectorCustomizers(connector -> {
    connector.setAllowTrace(true);
    });
    return tomcat;
    }

同等于传统的web.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<security-constraint>  
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>COPY</http-method>
<http-method>SEARCH</http-method>
<http-method>PROPFIND</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>
  1. 转发端口(http转到https)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Bean
public EmbeddedServletContainerFactory servletContainer() {
// 创建容器工厂
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
// 重写方法
@Override
protected void postProcessContext(Context context) {
// 新建一个安全设置
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
// 对与所有的请求
collection.addPattern("/*");
// 拦截验证这些方法的权限(即禁用)
collection.addMethod("HEAD");
collection.addMethod("PUT");
collection.addMethod("DELETE");
collection.addMethod("OPTIONS");
collection.addMethod("TRACE");
collection.addMethod("COPY");
collection.addMethod("SEARCH");
collection.addMethod("PROPFIND");
collection.addMethod("BOGUS");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
// 添加连接(除了默认的https连接,这里又监听了http的)
tomcat.addAdditionalTomcatConnectors(httpConnector());
return tomcat;
}

@Bean
public Connector httpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(httpPort);
connector.setSecure(false);
// 转发到https端口
connector.setRedirectPort(httpsPort);
return connector;
}

文章字数:2132,阅读全文大约需要8分钟

一、容器生命周期管理

命令 含义
run 启动新容器
start 启动一个或多个被停止的容器
stop 停止容器,允许容器进行关闭前准备
restart 重启容器
kill 直接关闭容器
rm 删除一个或多个容器
pause 暂停容器中所有的进程
unpause 恢复容器中所有的进程
create 创建新容器,但不启动
exec 在运行的容器中执行命令

## 1.1 run
  • 示例
1
docker run [options] imageTag [cmd] [args]
  • 参数说明
  1. -a stdin指定标准输出内容类型,可选stdin``stdout``stderr
  2. -d后台运行容器,返回容器ID
  3. -i交互式运行容器,通常搭配-t
  4. -t为容器重新分配伪输入终端,通常搭配-i
  5. -P随机映射端口,容器内端口随机映射到主机端口
  6. -p指定端口映射宿主端口:容器端口
  7. --name="name"指定容器名称
  8. --dns 8.8.8.8指定容器的DNS,默认和宿主机一致
  9. --dns-search test.com指定容器DNS搜索域,默认和宿主机一致
  10. -h "hostname"指定容器hostname
  11. -e username="name"设置环境变量
  12. --env-file=[]从指定文件中获取环境变量
  13. --cpuset="0-2"或者--cpuset="0,1,2"绑定容器到指定的CPU上运行
  14. -m设置容器使用内存最大值
  15. --net="bridge"指定容器网络连接类型,可选bridge``host``none``container
  16. --link=[]添加链接到另一个容器
  17. --expose=[]开发一个端口或者一组端口
  18. --volume``-v绑定一个卷

## 1.2 start
  • 示例
1
docker start [OPTIONS] CONTAINER [CONTAINER...]

## 1.3 stop
  • 示例
1
docker stop [OPTIONS] CONTAINER [CONTAINER...]

## 1.4 restart
  • 示例
1
docker restart [OPTIONS] CONTAINER [CONTAINER...]

## 1.5 kill
  • 示例
1
2
3
4
docker kill [OPTIONS] CONTAINER [CONTAINER...]

# -s 向容器发送一个信号
docker kill -s KILL mynginx

## 1.6 rm
  • 示例
1
docker rm [OPTIONS] CONTAINER [CONTAINER...]
  • 参数
  1. -f通过SIGKILL信号强制删除运行中的容器
  2. -l移除容器间的网络连接,而不是容器本身
  3. -v删除容器关联的卷

## 1.7 pause
  • 示例
1
docker pause CONTAINER [CONTAINER...]

## 1.8 unpause
  • 示例
1
docker unpause CONTAINER [CONTAINER...]

## 1.9 create
  • 示例
1
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]

## 1.10 exec
  • 示例
1
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

二、容器操作

命令 含义
ps 列出所有容器
inspect 获取容器/镜像的元数据
top 查看容器中运行的进程信息,支持ps命令参数
attach 连接到正在运行中的容器
events 从服务器获取实时事件
logs 获取容器的日志
wait 阻塞运行,直到容器停止。然后打印容器退出代码
export 将文件系统当做tar归档文件,导出到指定输出
port 列出容器端口映射

## 2.1 ps
  • 示例
1
docker ps [OPTIONS]
  • 参数
  1. -a显示所有的容器,包括未运行的
  2. -f根据条件过滤
  3. --format指定返回内容的模板
  4. -l显示最近创建的容器
  5. -n列出最近创建的n个容器
  6. --no-trunc不阶段输出
  7. -q静默模式,只显示容器编号
  8. -s显示总的文件大小

## 2.2 inspect
  • 示例
1
docker inspect [OPTIONS] NAME|ID [NAME|ID...]
  • 参数
  1. -f指定返回值的模板文件
  2. -s显示总的文件大小
  3. --type为指定类型返回JSON

## 2.3 top
  • 示例
1
docker top [OPTIONS] CONTAINER [ps OPTIONS]
  • 相当于在容器内部执行top命令

## 2.4 attach
  • 示例
1
docker attach [OPTIONS] CONTAINER
  • --sig-proxy=false参数可以使ctrl-dctrl-c不会退出容器,仅退出连接

## 2.5 events
  • 示例
1
docker events [OPTIONS]
  • 参数
  1. -f根据条件过滤
  2. --since从指定的时间戳后显示所有事件
  3. --until流水时间显示到指定时间为止

## 2.6 logs
  • 示例
1
docker logs [OPTIONS] CONTAINER
  • 参数
  1. -f跟着日志输出
  2. --since显示某个开始时间的所有日志
  3. -t显示时间戳
  4. --tail仅列出最新N条容器日志

## 2.7 wait
  • 示例
1
docker wait [OPTIONS] CONTAINER [CONTAINER...]

## 2.8 export
  • 示例
1
docker export [OPTIONS] CONTAINER
  • 参数
  1. -o将输出内容写到文件

## 2.9 port
  • 示例
1
2
docker port [OPTIONS] CONTAINER [PRIVATE_PORT[/PROTO]]
# docker port mymysql

三、容器rootfs命令

命令 含义
commit 从容器创建一个新的镜像,将容器保存成新镜像
cp 容器和主机直接数据拷贝
diff 检查容器里文件结构的更改

## 3.1 commit
  • 示例
1
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
  • 参数
  1. -a镜像作者
  2. -c使用Dockerfile来创建镜像
  3. -m镜像说明
  4. -p提交时将容器暂停

## 3.2 cp
  • 示例
1
2
docker cp [OPTIONS] 容器:地址 主机地址
docker cp [OPTIONS] 主机地址 容器:地址
  • -L保持源目标中的链接

## 3.3 diff
  • 示例
1
docker diff [OPTIONS] CONTAINER

四、镜像仓库

命令 含义
login/loginout 登录/退出镜像仓库
pull 从镜像仓库中拉取或者更新指定镜像
push 本地镜像上传到镜像仓库
search 从Docker Hub查找镜像

## 4.1 login
  • 示例
1
2
3
# -u 用户名 -p 密码 不指定仓库地址,默认是Docker Hub
docker login [OPTIONS] [SERVER]
docker logout [OPTIONS] [SERVER]

## 4.2 pull
  • 示例
1
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
  • 参数
  1. -a拉取所有tagged镜像
  2. --disable-content-trust忽略镜像校验,默认开启

## 4.3 push
  • 示例
1
2
# --disable-content-trust :忽略镜像的校验,默认开启
docker push [OPTIONS] NAME[:TAG]

## 4.4 search
  • 示例
1
docker search [OPTIONS] TERM
  • 参数
  1. --automated只列出automated build类型的镜像
  2. --no-trunc显示完整镜像描述
  3. -f stars=10列出收藏数不小于指定数的镜像

五、本地仓库

命令 示例 含义
images 列出本地镜像
rmi 删除本地一个或多个镜像
tag 标记本地镜像,将其归入某一仓库
build 使用Dockerfile创建镜像
history 查看指定镜像的创建历史
save 和export类似,生产的文件是多层,可以回滚到上个版本
load
import

## 5.1 images
  • 示例
1
docker images [OPTIONS] [REPOSITORY[:TAG]]
  • 参数
  1. -a列出本地所有镜像(含中间影响层,默认没有)
  2. --digests显示镜像摘要
  3. -f显示满足条件的镜像
  4. --format指定返回值的模板文件
  5. --no-trunc显示完整的镜像信息
  6. -q只显示镜像ID

## 5.2 rmi
  • 示例
1
docker rmi [OPTIONS] IMAGE [IMAGE...]
  • 参数
  1. -f强制删除
  2. --no-prune不移除该镜像的过程镜像,默认移除

## 5.3 tag
  • 示例
1
docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

## 5.4 build
  • 示例
1
2
docker build [OPTIONS] PATH | URL | -
# docker build -t runoob/ubuntu:v1 .
  • 参数
  1. --build-arg=[]设置镜像创建时的变量
  2. --cpu-shares设置cpu使用权重
  3. --cpu-period限制cpu cfs周期
  4. --cpu-quota限制cpu cfs配额
  5. --cpuset-cpus指定使用的cpu id
  6. --cpuset-mens指定使用的内存id
  7. --disable-content-trust忽略检验,默认开启
  8. -f指定使用的Dockerfile路径,.为本目录下的dockerfile
  9. --force-rm设置镜像过程中删除中间容器
  10. --isolation使用容器隔离技术
  11. --label=[]设置镜像使用的元数据
  12. -m设置内存最大值
  13. --memory-swap设置swap最大值为内存+swap,-1表示不限制swap
  14. --no-cache创建镜像的过程中不适用缓存
  15. --pull尝试更新镜像的新版本
  16. --quiet,-q安静模式,成功后只输出镜像ID
  17. --rm设置镜像成功后删除中间容器
  18. --shm-size设置/dev/shm的大小,默认64m
  19. --ulimit设置Ulimit配置
  20. --squashDockerfile中所有操作压缩为一层
  21. --tag,-t设置镜像名字及标签,通常name:tagname,可以在一次构建中为一个镜像设置多个标签
  22. --network默认default在构建期间设置RUN指令的网络模式

## 5.5 history
  • 示例
1
docker history [OPTIONS] IMAGE
  • 参数
  1. -H以可读的格式打印镜像大小和日期,默认true
  2. --no-trunc显示完整提交记录
  3. -q仅列出提交记录ID

其他

命令 示例 含义
info docker info [OPTIONS] 查看docker系统、镜像、容器数等信息
version docker version 显示docker版本信息

文章字数:1377,阅读全文大约需要5分钟

2014年3月发布,主要增加了lambda表达式,stream,新Date类

Lambda

  1. 含义:将函数作为参数传入方法,方法内可以在适当的时候执行此函数。

    1
    //Lambda函数对应一个@FunctionalInterface注解的接口,本质是实现此接口的方法。接口只能有一个对外方法。
  2. Lambda表达式

1
2
3
map.forEach((k,v)->{
System.out.println(k);
});
  1. 简写
1
map.forEach((k,v)->System.out.println(k));

StreamAPI

list.stream()将数据转换成流的形式,然后就可以对于流进行链式操作,其中操作分为中间操作和结束操作,结束操作只能有一个。

  1. 流有原始流(IntStream…)boxed流(Stream)
1
2
IntStream.boxed();//转换
Stream.mapToInt()...//转换
  1. 原始流没有数据收集的方法(.collect(Collectors.toList()))可以转换成boxed流然后收集

  2. 除了通过数据生成流,也可以随机生成

1
IntStream.range(1, 100).boxed().collect(Collectors.toList());
序号 中间操作 结束操作
1 filter foreach
2 map、mapToInt、mapToDouble、mapToLong collect
3 flatMap、flatMapToInt、flatMapToLong、flatMapToDouble anyMatch
4 distinct allMatch
5 sorted noneMatch
6 skip reduce
7 peek min
8 limit max
9 of count
10 iterate toArray
11 generate forEachOrdered
12 empty findFirst
13 concat findAny

中间操作

  1. filter过滤
1
2
//返回true的方向,否则拦截
list.stream().filter(e->e.equals("1")).forEach(System.out::println);
  1. map转换集合
    同类有:mapToIntmapToDoublemapToLong
1
2
3
//int类型的id转换成string,即方法接收值之后可以返回其它类型的值并传递下去
idList.stream().map(Student::getName).collect(Collectors.toList())
.forEach(System.out::println);

mapToInt

1
IntStream intStream = list.stream().mapToInt(Student::getAge);
  1. flatMap将流中的元素也转换成流,加入到流中
    同类有:flatMapToIntflatMapToLongflatMapToDouble
1
2
3
4
5
List<String> list2 = new ArrayList<>();
list2.add("aaa bbb ccc");
list2.add("ddd eee fff");
list2.add("ggg hhh iii");
list2 = list2.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(Collectors.toList());
  1. distinct去重
1
2
3
4
list.add("1");
list.add("1");
list.add("2");
list.stream().distinct().forEach(System.out::println);
  1. sorted排序
1
2
3
4
5
//asc排序
list.stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);
System.out.println("------------------------------------------------------------------");
//desc排序
list.stream().sorted(Comparator.comparingInt(Student::getAge).reversed()).forEach(System.out::println);
  1. skip跳过前n个
1
list.stream().skip(1).forEach(System.out::println);
  1. peek生成副本
1
2
3
4
5
6
7
8
9
//和forEach类似,只是不影响源数据,并且是中间操作
List<String> list = new ArrayList();
list.add("123");
list.add("456");
List l = list.stream().peek(v-> {
v = v + "zzz";
}).collect(Collectors.toList());

l.forEach(System.out::println);
  1. limit截取前n个
1
list.stream().limit(1).forEach(System.out::println);
  1. of生成流

  2. iterate生成流

1
2
3
4
5
List<String> list = Arrays.asList("a", "b", "c", "c", "d", "f", "a");
//从0开始,每次加1,生成list长度的流
Stream.iterate(0, i -> i + 1).limit(list.size()).forEach(i -> {
System.out.println(String.valueOf(i) + list.get(i));
});
  1. generate生成指定流
1
2
Stream<String> stream = Stream.generate(()->"user").limit(20);
stream.forEach(System.out::println);
  1. empty生成空流

  2. concat连接两个流

1
2
List<String> list = Arrays.asList("a", "b", "c", "c", "d", "f", "a");
Stream.concat(list.stream(),list.stream()).forEach(System.out::println);

结束操作

  1. foreach 遍历
1
2
//直接将值作为函数的输入值可以使用 类::方法 的形式
list.forEach(System.out::println);
  1. collect转换成集合
1
2
//过滤空元素
list.filter(StringUtil::isNotBlank).collect(Collectors.toList());
  1. anyMatch是否有一个满足条件
1
boolean isHave = list.stream().anyMatch(student -> student.getAge() == 16);
  1. allMatch是否全部满足
1
boolean isHave = list.stream().allMatch(student -> student.getAge() == 16);
  1. noneMatch都不符合
1
boolean isHave = list.stream().noneMatch(student -> student.getAge() == 16);
  1. reduce每个元素的返回值传入下个元素中
1
2
3
4
5
6
7
8
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
// 计算总和,count是每次返回的数,都会传入下一个元素里。
// v是当前元素的值。0是count的初始值,没有初始化值返回的是Optional
int total = list.stream().reduce(0,(count,v)->{
return count+v;
});
  1. min最小的

  2. max最大的

  3. count计数

1
long count = list.stream().count();
  1. toArray转换成数组
1
2
toArray(Integer[] :: new)//转换成Interger[]
toArray(person[] :: new)//对象数组
  1. forEachOrdered遍历,顺序一定和元素顺序一样
1
2
//和forEach一样,遍历流,不过这个执行顺序一定和流顺序一样
list.forEachOrdered();
  1. findFirst 找到第一个元素
1
Optional<Student> student = list.stream().findFirst();
  1. findAny 找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素)
1
Optional<Student> student = list.stream().findAny();

新增了时间类型

  1. 新增的api
  • 本地日期和时间LocalDateTime LocalDate LocalTime
    本地时间指的是经过时区偏移量计算过的时间,其本身不带时区内容。
    比如12:00,无法分辨是那个时区的12:00.

  • 带有时区的日期时间ZonedDateTime
    本地时间通过设置时区或偏移可以换算成这个。
    时间戳设置时区或偏移量也可以转换。

  • 时刻(时间戳)Instant

  • 时区ZoneId ZoneOffset

  • 时间间隔Duration

  • 线程安全的格式化工具DateTimeFormatter

  1. 新API修正了旧API不合理的常量设计:
  • Month的范围用1~12表示1月到12月;
  • Week的范围用1~7表示周一到周日。

时间类应用

  1. 毫秒数转时间
1
2
3
4
5
6
7
8
// 1. 声明可复用的时间格式化工具
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

// 2. 毫秒转时间戳,时间戳设置时区成为带时区的时间类型(使用默认时区),带时区的时间类型转换成本地时间
LocalDate localDate = Instant.ofEpochMilli(millisecond).atZone(ZoneId.systemDefault()).toLocalDate();

// 3. 格式化本地时间
formatter.format(localDate)
  1. 获取当天时间(自带格式化)
1
2
3
// 现在的本地时间是 : 2019-01-05
LocalDate today = LocalDate.now();
System.out.println("现在的本地时间是 : " + today);
  1. 直接获取当前
  • 年月日

    1
    2
    3
    4
    5
    LocalDate today = LocalDate.now();
    int year = today.getYear();
    int month = today.getMonthValue();
    int day = today.getDayOfMonth();
    System.out.printf("年: %d 月 : %d 日 : %d ", year, month, day);
  • 时间

1
2
LocalTime time = LocalTime.now();
System.out.println("当前时间是 : " + time);
  1. 构造指定日期
1
2
LocalDate dateOfBirth = LocalDate.of(2018, 04, 01);
System.out.println("你的生日是 : " + dateOfBirth);
  1. 日期比较
  • 比较年月日都是否相等

    1
    2
    3
    LocalDate date1 = LocalDate.of(2018, 04, 01);
    LocalDate today = LocalDate.now();
    System.out.println("date1和今天是同一天吗?:"+ date1.equals(today));
  • 忽略年

1
2
3
4
LocalDate dateOfBirth = LocalDate.of(2018, 04, 01);
MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(today);
System.out.println("今天是4月1日吗?" + currentMonthDay.equals(birthday));
  • 大小比较
1
2
3
4
LocalDate tomorrow = LocalDate.of(2018, 4, 2);
System.out.println("2018-04-02是未来的时间吗?" + tommorow.isAfter(today));
LocalDate yesterday = today.minus(1, DAYS);
System.out.println("2018-03-31是过去的时间吗?" + yesterday.isBefore(today));
  1. 日期增加/减少
  • 加小时
1
2
3
LocalTime time = LocalTime.now();
LocalTime newTime = time.plusHours(2); // 加2个小时
System.out.println("两个小时后是 : " + newTime);
  • 加一周
1
2
3
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
System.out.println("一星期后 : " + nextWeek);
  • 加一年
1
today.minus(1, ChronoUnit.YEARS);
  1. 获取时区
1
2
3
ZoneId zId = ZoneId.of("GMT+8");
LocalDateTime localtDateAndTime = LocalDateTime.now();
ZonedDateTime bj = ZonedDateTime.of(localtDateAndTime, zId );
  1. 是否是闰年
1
System.out.print("今年2019是闰年?" + today.isLeapYear());
  1. 两个日期直接有多少天和月
1
2
3
4
LocalDate day = LocalDate.of(2018, Month.APRIL, 1);
LocalDate today = LocalDate.now();
Period period = Period.between(day , today);
System.out.println("2018年4月1日距离今天过去了多少个月: " + period.getMonths());
  1. 时区偏移量的日期时间
1
2
3
4
5
6
7
LocalDateTime datetime = LocalDateTime.of(2018, Month.JANUARY, 14, 19, 30);
ZoneOffset offset = ZoneOffset.of("+05:30");
// 给机器看的
OffsetDateTime date = OffsetDateTime.of(datetime, offset);
System.out.println("带时区的日期和时间 : " + date);
// 这个是格式化之后给人看的
ZoneDateTime date = ZoneDateTime.of(datetime, offset);

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

反射是在软件运行时可以获取类信息的一种java机制

获取类信息的方式

有两种:

  1. RTTI,编译器在编译时打开和检查.class文件
  2. 反射,运行时打开和检查.class文件

反射获取信息的代码

1
2
3
4
5
6
7
8
9
public void testInvoke() throws Exception {
Class clazz = Main.class;
Object object = clazz.newInstance();
Field[] fields = clazz.getFields();
Method[] methods = clazz.getMethods();
Method method = clazz.getDeclaredMethod("sayHello", String.class, String.class);
method.setAccessible(true);
method.invoke(object,"324","345");
}

method.setAccessible(true)在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。私有方法不用开启。


文章字数:28,阅读全文大约需要1分钟
数据类型| 占用空间(byte)
:—|:—
boolean|1
byte|1
short|2
char|2
int|4
float|4
long|8
double|8


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

常见的有Serial GCParNew GCCMS GCParallel GCG1 GC

Serial GC

最古老的GC,单线程。client模式下默认选项,采用复制算法

  1. 优点
  • 结构简单,初始化简单
  1. 缺点
  • Stop-The World时间较长

ParNew GC

SerialGC的多线程版本,新生代GC的实现
最常见的是配合老年代CMS GC工作

Parrallel GC

Parallel Scavenge (新生代) + Parallel Old (老年代)
在JDK8等版本中,是server模式JVM的默认GC选择,也被称为吞吐量优先的GC,算法和Serial GC相似,特点是老生代和新生代GC并行进行,更加高效。

CMS(Concurrent Mark Sweep)GC

基于标记-清除(Mark-Sweep)算法,尽量减少停顿时间

优点: 基于标记-清除(Mark-Sweep)算法,尽量减少停顿时间。
缺点: 存在碎片化问题,在长时间运行的情况下会发生full GC,导致恶劣停顿。会占用更多的CPU资源,和用户争抢线程。在JDK 9中被标记为废弃。

G1 GC

兼顾了吞吐量和停顿时间的GC实现,是Oracle JDK 9后默认的GC
可以直观的设值停顿时间,相对于CMS GC ,G1未必能做到CMS最好情况下的延时停顿,但比最差情况要好得多
G1 仍存在年代的概念,使用了Region棋盘算法,实际上是标记-整理(Mark-Compact)算法,可以避免内存碎片,尤其是堆非常大的时候,G1优势更明显。
G1 吞吐量和停顿表现都非常不错。


文章字数:937,阅读全文大约需要3分钟

java程序通过java -option启动,其中java程序为jdk安装目录下bin文件夹中的java可执行程序,-option为虚拟机参数。

java运行参数分为三大类

  1. 基本参数: -所有虚拟机都需要具备的功能,并且向后兼容
  2. 非标准参数: -X默认jvm都实现这些功能但是不保证所有的jvm都能满足,且不保证向后兼容
  3. 非Stable参数: -XX不稳定参数,各个jvm实现会不相同,未来随时会取消。慎重使用

标准参数

参数 描述
-client 设置虚拟机使用client模式启动,运行速度快,但是性能和内存管理效率不如server。
-server 设置虚拟机使用server模式启动,64位下默认启动,忽略-client
-classpath或-cp 告知虚拟机搜索的目录、jar文件、zip文档名,用;隔开。注1
-D=value 在虚拟机系统属性中设置属性名/值的键值对,可在运行时System.getProperty("propertyName")获取值,有空格需要使用双引号-Dname=”space string”
-verbose/-verbose:class 输出虚拟机装入的类的信息,显示格式注2
-verbose:gc 虚拟机内存回收时在输出设备显示信息,格式注3
-verbose:jni 虚拟机调用native方法时输出信息,格式注4
-version 显示可运行虚拟机版本信息然后退出
-showversion 显示产品版本并继续
-ea 开启断言,同下
-enableassertions[: :]
-da/disableassertions 关闭断言,其它同上
-esa/enablesystemassertions 设置虚拟机显示系统类的断言
-dsa/disablesystemassertions 关闭系统类的断言
-agentlib:[=] 虚拟机装载本地代理库,参数未name=xx=1,yy=2这样的格式。windows下找libname.dll,unix下找libname.so文件。搜索路径不同系统不一样。注5
-agentpath:[=] 设置虚拟机本地库路径,其它和上面一样
-javaagent:[=] 设置启动时装入java语言的设备代理,注6

注:

  1. -classpath:虚拟机在运行一个类时,需要将其装入内存虚拟机搜索的顺序为Bootstrap classes-Extension classes-User classes。其中

    1
    2
    3
    4
    1. `Bootstrap classes`是虚拟机自带的 `jar`或者`zip`文件,虚拟机搜索这些包的文件,使用`System.getProperty("sun.boot.class.path")`可以得到虚拟机所搜的包名。
    2. `Extension classes`是位于`jre` `lib` `ext`下的jar文件,使用`System.getProperty("java.ext.dirs")`可获得`Extension`搜索路径
    3. `User classes`搜索的顺序为当前目录,环境变量CLASSPATH、-classpath
    使用`-classpath`后虚拟机不再使用环境变量`CLASSPATH`作为搜索路径。
  2. -verbose:class:展示类信息为:

    1
    2
    [Loaded java.io.FilePermission$1 from shared objects file]
    当虚拟机报告类找不到或类冲突时可用此参数来诊断来查看虚拟机从装入类的情况
  3. -verbose:gc:显示gc格式如下

    1
    2
    [Full GC 268K->168K(1984K), 0.0187390 secs]
    该参数用来监视虚拟机内存回收的情况。
  4. -verbose:jni:显示jvm调用native方法时输出信息格式

    1
    2
    [Dynamic-linking native method HelloNative.sum ... JNI]
    该参数用来监视虚拟机调用本地方法的情况,在发生jni错误时可为诊断提供便利。
  5. -agentlib:<libname>[=<options>]:

    1
    例如可使用-agentlib:hprof来获取虚拟机的运行情况,包括CPU、内存、线程等的运行数据,并可输出到指定文件中,可用-agentlib:hprof=help来得到使用帮助列表。在jre"bin目录下可发现hprof.dll文件。
  6. -javaagent:<jarpath>[=<options>]:

    1
    虚拟机启动时装入java语言设备代理。Jarpath文件中的mainfest 文件必须有Agent-Class属性。代理类要实现public static void premain(String agentArgs, Instrumentation inst)方法。当虚拟机初始化时,将按代理类的说明顺序调用premain方法。