springboot+jwt+vue+redis单点登录

单点登录

概念

在微服务或分布式架构下,用户的登录实现只需要登录一次,而登录信息在各个独立的模块下可以共用。

流程

1、前端登录,将登录信息传给后端。

2、后端验证登录信息后根据登录信息生成token,将用户信息、token存储于redis缓存中,并将token返还给前端。

3、前端拿到token后间其存储起来(cookie),并在每一次向后端发出请求时给请求头加上token。

4、后端拦截请求,获取请求头里的token,验证token的有效性。

实现

新建前后端项目

不做赘述

后端

pom(子模块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>

<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!--Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>

<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

yaml

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
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcxxxxxxxxxxxxxxxxxxx
# 令牌有效期(默认30分钟)
expireTime: 120

spring:
# redis 配置
redis:
# 地址
host: 127.0.0.1
# 端口,默认为6379
port: 6379
# 密码
password: 123456
# 连接超时时间
timeout: 30s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
database: 11

实体类

登录实体类不做赘述

登录模块

controller

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
@RestController
public class LoginController
{
@Autowired
private LoginService loginService;

@Autowired
private TokenService tokenService;

/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@PostMapping("/login")
public HashMap login(@RequestBody LoginBody loginBody)
{
HashMap<String,Object> ajax = new HashMap<>();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid());
ajax.put("token", token);
return ajax;
}
}

LoginService

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
/**
* 登录校验方法
*/
@Component
public class LoginService
{
@Autowired
private TokenService tokenService;

@Autowired
public RedisTemplate redisTemplate;

/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid)
{

String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String captcha = redisTemplate.getStringSerializer(verifyKey);
redisTemplate.delete(verifyKey);
if (captcha == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}

// 用户验证
/*
用户验证模块自己写,爱怎么写怎么写,数据库里查出来验证也行
*/

return tokenService.createToken(loginUser);
}
}

TokenService

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
/**
* token验证处理
*/
@Component
public class TokenService
{
// 令牌自定义标识
@Value("${token.header}")
private String header;

// 令牌秘钥
@Value("${token.secret}")
private String secret;

// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int expireTime;

protected static final long MILLIS_SECOND = 1000;

protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

@Autowired
private RedisTemplate redisTemplate;

/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims)
{
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}

/**
* 创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser)
{
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
refreshToken(loginUser);
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims);
}

/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request)
{
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token))
{
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(userKey);
}
return null;
}

/**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request)
{
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
{
token = token.replace(Constants.TOKEN_PREFIX, "");
}
return token;
}

/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisTemplate.opsForValue().set(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}

/**
* 验证令牌有效期,相差不足20分钟,自动刷新缓存
*
* @param loginUser
* @return 令牌
*/
public void verifyToken(LoginUser loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
{
refreshToken(loginUser);
}
}
}

请求过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* token过滤器 验证token有效性
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private TokenService tokenService;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
{
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
}
}

若模块间需要相互调用,需要做token传递,这里以RestTemplate为例。

1
2
3
4
5
6
7
8
9
10
@Component
public class TokenInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("Authorization", "你的token,可以登录时预存在redis里这里取出来");
return execution.execute(request, body);
}
}

前端

登陆事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 登录
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
},

request.js拦截器

1
2
3
4
5
6
7
8
9
10
11
12
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})

token.js

1
2
3
4
5
6
7
8
9
10
11
export function getToken() {
return Cookies.get(TokenKey)
}

export function setToken(token) {
return Cookies.set(TokenKey, token)
}

export function removeToken() {
return Cookies.remove(TokenKey)
}

前端能力还不足,只给出部分有关代码。

阅读全文
c++实现简单的万年历打印

c++实现简单的万年历打印

代码

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
#include<iostream>
using namespace std;
int runnian(int nian)//判断是否为闰年
{
int i=1;
if((nian%4==0&&nian%100!=0)||nian%400==0)
i=2;
return i;
}

int zhouji(int nian)//计算元旦是周几
{
int n=0,zhouji,n1=1900;
if(nian>=1900)
{
for(;n1<nian;n1++)
n+=runnian(n1);
zhouji=(n%7)+1;
}
if(nian<1900)
{
for(;nian<1900;nian++)
n+=runnian(n1);
zhouji=8-(n%7);
}
return zhouji;
}

void dayin(int nian,int n)
{
int i,j=0,k,l=n,m,p;
if(runnian(nian)==1)//非闰年
m=28;
if(runnian(nian)==2)//闰年
m=29;
for(i=1;i<=12;i++)
{
if(i==2)
p=m;
if((i%2==1&&i<9)||i==8||i==10||i==12)
p=31;
if(i%2==0||i==9||i==11)
p=30;
cout<<endl<<i<<"月"<<endl;
cout<<"--------------------------------------------------------------------"<<endl;
cout<<"星期一\t星期二\t星期三\t星期四\t星期五\t星期六\t星期天"<<endl;
cout<<"--------------------------------------------------------------------"<<endl;
for(;l>1;l--)
cout<<"\t";
for(k=1;k<=p;k++)
{
n++;
cout<<k<<"\t";
if(n==8)
{
cout<<endl;
n=1;
}
}
cout<<endl;
l=n;
}
}

int main()
{
int nian;
cout<<"请输入年号:";
cin>>nian;
dayin(nian,zhouji(nian));
return 0;
}

运行

打印

阅读全文
c++实现简单坦克大战游戏

c++实现简单坦克大战游戏

代码

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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# include <stdio.h>
# include <string.h>
# include <windows.h>
# include <stdlib.h>
# include <conio.h>
# include <time.h>
# include <math.h>
# include<iostream>
using namespace std;

#define wide 20
#define length 30

char map[wide][length];
char tank[wide][length];
char ctank[wide][length];
char bullet[wide][length];
char cbullet[wide][length];
int center_x=wide/2,center_c1x;
int center_y=length/2,center_c1y;
char dir='M',c1dir='K',cdir;
char x='2';
int n=-1;

void makemap(char map[wide][length])
{
int i,j;
for(i=0;i<wide;i++)
for(j=0;j<length;j++)
{
if(i==0||j==0||i==wide-1||j==length-1)
map[i][j]='#';
}
}

void maketank(char tank[wide][length])
{
tank[center_x][center_y]='0';
tank[center_x][center_y+1]='0';
tank[center_x+1][center_y]='0';
tank[center_x-1][center_y]='0';
tank[center_x-1][center_y-1]='0';
tank[center_x+1][center_y-1]='0';
}

void makectank(char ctank[wide][length],char tank[wide][length])
{
if(x>'1')
{
int i,j;
center_c1x=rand()%(wide-4)+2;
center_c1y=rand()%(length-4)+2;
for(i=center_c1x-1;i<=center_c1x+1;i++)
for(j=center_c1y-1;j<=center_c1y+1;j++)
if(ctank[i][j]!=0||tank[i][j]!=0)
{
center_c1x=rand()%(wide-4)+2;
center_c1y=rand()%(length-4)+2;
i=center_c1x-1;
}
ctank[center_c1x][center_c1y]='0';
ctank[center_c1x][center_c1y-1]='0';
ctank[center_c1x+1][center_c1y]='0';
ctank[center_c1x-1][center_c1y]='0';
ctank[center_c1x-1][center_c1y+1]='0';
ctank[center_c1x+1][center_c1y+1]='0';
x--;
n++;
}
}

void makebullet(char bullet[wide][length],int center_x,int center_y)
{
if(dir=='H')
bullet[center_x-1][center_y]='1';
if(dir=='P')
bullet[center_x+1][center_y]='2';
if(dir=='K')
bullet[center_x][center_y-1]='3';
if(dir=='M')
bullet[center_x][center_y+1]='4';
}

char getdir()
{
char ch;
if(kbhit())
{
ch=getch();
return ch;
}
}

void makecbullet(int center_cx,int center_cy,char cdir,char cbullet[wide][length])
{
if(cdir=='H')
cbullet[center_cx-1][center_cy]='1';
if(cdir=='P')
cbullet[center_cx+1][center_cy]='2';
if(cdir=='K')
cbullet[center_cx][center_cy-1]='3';
if(cdir=='M')
cbullet[center_cx][center_cy+1]='4';
}

void getcdir()
{
int i;
i=rand()%5;
switch(i)
{
case 0:cdir='H';
break;
case 1:cdir='P';
break;
case 2:cdir='K';
break;
case 3:cdir='M';
break;
case 4:makecbullet(center_c1x,center_c1y,c1dir,cbullet);
break;
}
}

void movebullet(char bullet[wide][length],char ctank[wide][length],char cbullet[wide][length])
{
int i,j,a,b;
for(i=0;i<wide;i++)
for(j=0;j<length;j++)
{
if(bullet[i][j]=='1')
{
if(ctank[i][j]!=0)
{
for(a=0;a<wide;a++)
for(b=0;b<length;b++)
ctank[a][b]=0;
bullet[i][j]=0;
x++;
}
else if(i>1)
bullet[i-1][j]=bullet[i][j];
bullet[i][j]=0;
}
if(bullet[wide-i][length-j]=='2')
{
if(ctank[wide-i][length-j]!=0)
{
for(a=0;a<wide;a++)
for(b=0;b<length;b++)
ctank[a][b]=0;
bullet[wide-i][length-j]=0;
x++;
}
else if(i>2)
bullet[wide-i+1][length-j]=bullet[wide-i][length-j];
bullet[wide-i][length-j]=0;
}
if(bullet[i][j]=='3')
{
if(ctank[i][j]!=0)
{
for(a=0;a<wide;a++)
for(b=0;b<length;b++)
ctank[a][b]=0;
bullet[i][j]=0;
x++;
}
else if(j>1)
bullet[i][j-1]=bullet[i][j];
bullet[i][j]=0;
}
if(bullet[wide-i][length-j]=='4')
{
if(ctank[wide-i][length-j]!=0)
{
for(a=0;a<wide;a++)
for(b=0;b<length;b++)
ctank[a][b]=0;
bullet[wide-i][length-j]=0;
x++;
}
else if(j>2)
bullet[wide-i][length-j+1]=bullet[wide-i][length-j];
bullet[wide-i][length-j]=0;
}
}
}

void run(char ch,char map[wide][length],char tank[wide][length])
{
if(ch==dir)
{
if(ch=='H')
{
if(map[center_x-2][center_y]=='#')
return;
tank[center_x-2][center_y-1]=tank[center_x-1][center_y-1];
tank[center_x-2][center_y]=tank[center_x-1][center_y];
tank[center_x-2][center_y+1]=tank[center_x-1][center_y+1];
tank[center_x-1][center_y-1]=tank[center_x][center_y-1];
tank[center_x-1][center_y]=tank[center_x][center_y];
tank[center_x-1][center_y+1]=tank[center_x][center_y+1];
tank[center_x][center_y-1]=tank[center_x+1][center_y-1];
tank[center_x][center_y]=tank[center_x+1][center_y];
tank[center_x][center_y+1]=tank[center_x+1][center_y+1];
tank[center_x+1][center_y-1]=0;
tank[center_x+1][center_y]=0;
tank[center_x+1][center_y+1]=0;
center_x--;
}
if(ch=='P')
{
if(map[center_x+2][center_y]=='#')
return;
tank[center_x+2][center_y-1]=tank[center_x+1][center_y-1];
tank[center_x+2][center_y]=tank[center_x+1][center_y];
tank[center_x+2][center_y+1]=tank[center_x+1][center_y+1];
tank[center_x+1][center_y-1]=tank[center_x][center_y-1];
tank[center_x+1][center_y]=tank[center_x][center_y];
tank[center_x+1][center_y+1]=tank[center_x][center_y+1];
tank[center_x][center_y-1]=tank[center_x-1][center_y-1];
tank[center_x][center_y]=tank[center_x-1][center_y];
tank[center_x][center_y+1]=tank[center_x-1][center_y+1];
tank[center_x-1][center_y-1]=0;
tank[center_x-1][center_y]=0;
tank[center_x-1][center_y+1]=0;
center_x++;
}
if(ch=='K')
{
if(map[center_x][center_y-2]=='#')
return;
tank[center_x-1][center_y-2]=tank[center_x-1][center_y-1];
tank[center_x][center_y-2]=tank[center_x][center_y-1];
tank[center_x+1][center_y-2]=tank[center_x+1][center_y-1];
tank[center_x-1][center_y-1]=tank[center_x-1][center_y];
tank[center_x][center_y-1]=tank[center_x][center_y];
tank[center_x+1][center_y-1]=tank[center_x+1][center_y];
tank[center_x-1][center_y]=tank[center_x-1][center_y+1];
tank[center_x][center_y]=tank[center_x][center_y+1];
tank[center_x+1][center_y]=tank[center_x+1][center_y+1];
tank[center_x-1][center_y+1]=0;
tank[center_x][center_y+1]=0;
tank[center_x+1][center_y+1]=0;
center_y--;
}
if(ch=='M')
{
if(map[center_x][center_y+2]=='#')
return;
tank[center_x-1][center_y+2]=tank[center_x-1][center_y+1];
tank[center_x][center_y+2]=tank[center_x][center_y+1];
tank[center_x+1][center_y+2]=tank[center_x+1][center_y+1];
tank[center_x-1][center_y+1]=tank[center_x-1][center_y];
tank[center_x][center_y+1]=tank[center_x][center_y];
tank[center_x+1][center_y+1]=tank[center_x+1][center_y];
tank[center_x-1][center_y]=tank[center_x-1][center_y-1];
tank[center_x][center_y]=tank[center_x][center_y-1];
tank[center_x+1][center_y]=tank[center_x+1][center_y-1];
tank[center_x-1][center_y-1]=0;
tank[center_x][center_y-1]=0;
tank[center_x+1][center_y-1]=0;
center_y++;
}
}
if(ch!=dir)
{
if(ch=='H')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x-1][center_y]='0';
tank[center_x][center_y-1]='0';
tank[center_x][center_y]='0';
tank[center_x][center_y+1]='0';
tank[center_x+1][center_y-1]='0';
tank[center_x+1][center_y+1]='0';
dir='H';
}
if(ch=='P')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x+1][center_y]='0';
tank[center_x][center_y-1]='0';
tank[center_x][center_y]='0';
tank[center_x][center_y+1]='0';
tank[center_x-1][center_y-1]='0';
tank[center_x-1][center_y+1]='0';
dir='P';
}
if(ch=='K')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x][center_y-1]='0';
tank[center_x-1][center_y]='0';
tank[center_x][center_y]='0';
tank[center_x+1][center_y]='0';
tank[center_x-1][center_y+1]='0';
tank[center_x+1][center_y+1]='0';
dir='K';
}
if(ch=='M')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
{
tank[i][j]=0;
}
tank[center_x][center_y+1]='0';
tank[center_x-1][center_y]='0';
tank[center_x][center_y]='0';
tank[center_x+1][center_y]='0';
tank[center_x-1][center_y-1]='0';
tank[center_x+1][center_y-1]='0';
dir='M';
}
}
}

void crun(char ch,char map[wide][length],char tank[wide][length],int center_x,int center_y)
{
if(ch==c1dir)
{
if(ch=='H')
{
if(map[center_x-2][center_y]=='#')
return;
tank[center_x-2][center_y-1]=tank[center_x-1][center_y-1];
tank[center_x-2][center_y]=tank[center_x-1][center_y];
tank[center_x-2][center_y+1]=tank[center_x-1][center_y+1];
tank[center_x-1][center_y-1]=tank[center_x][center_y-1];
tank[center_x-1][center_y]=tank[center_x][center_y];
tank[center_x-1][center_y+1]=tank[center_x][center_y+1];
tank[center_x][center_y-1]=tank[center_x+1][center_y-1];
tank[center_x][center_y]=tank[center_x+1][center_y];
tank[center_x][center_y+1]=tank[center_x+1][center_y+1];
tank[center_x+1][center_y-1]=0;
tank[center_x+1][center_y]=0;
tank[center_x+1][center_y+1]=0;
center_c1x--;
}
if(ch=='P')
{
if(map[center_x+2][center_y]=='#')
return;
tank[center_x+2][center_y-1]=tank[center_x+1][center_y-1];
tank[center_x+2][center_y]=tank[center_x+1][center_y];
tank[center_x+2][center_y+1]=tank[center_x+1][center_y+1];
tank[center_x+1][center_y-1]=tank[center_x][center_y-1];
tank[center_x+1][center_y]=tank[center_x][center_y];
tank[center_x+1][center_y+1]=tank[center_x][center_y+1];
tank[center_x][center_y-1]=tank[center_x-1][center_y-1];
tank[center_x][center_y]=tank[center_x-1][center_y];
tank[center_x][center_y+1]=tank[center_x-1][center_y+1];
tank[center_x-1][center_y-1]=0;
tank[center_x-1][center_y]=0;
tank[center_x-1][center_y+1]=0;
center_c1x++;
}
if(ch=='K')
{
if(map[center_x][center_y-2]=='#')
return;
tank[center_x-1][center_y-2]=tank[center_x-1][center_y-1];
tank[center_x][center_y-2]=tank[center_x][center_y-1];
tank[center_x+1][center_y-2]=tank[center_x+1][center_y-1];
tank[center_x-1][center_y-1]=tank[center_x-1][center_y];
tank[center_x][center_y-1]=tank[center_x][center_y];
tank[center_x+1][center_y-1]=tank[center_x+1][center_y];
tank[center_x-1][center_y]=tank[center_x-1][center_y+1];
tank[center_x][center_y]=tank[center_x][center_y+1];
tank[center_x+1][center_y]=tank[center_x+1][center_y+1];
tank[center_x-1][center_y+1]=0;
tank[center_x][center_y+1]=0;
tank[center_x+1][center_y+1]=0;
center_c1y--;
}
if(ch=='M')
{
if(map[center_x][center_y+2]=='#')
return;
tank[center_x-1][center_y+2]=tank[center_x-1][center_y+1];
tank[center_x][center_y+2]=tank[center_x][center_y+1];
tank[center_x+1][center_y+2]=tank[center_x+1][center_y+1];
tank[center_x-1][center_y+1]=tank[center_x-1][center_y];
tank[center_x][center_y+1]=tank[center_x][center_y];
tank[center_x+1][center_y+1]=tank[center_x+1][center_y];
tank[center_x-1][center_y]=tank[center_x-1][center_y-1];
tank[center_x][center_y]=tank[center_x][center_y-1];
tank[center_x+1][center_y]=tank[center_x+1][center_y-1];
tank[center_x-1][center_y-1]=0;
tank[center_x][center_y-1]=0;
tank[center_x+1][center_y-1]=0;
center_c1y++;
}
}
if(ch!=c1dir)
{
if(ch=='H')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x-1][center_y]='0';
tank[center_x][center_y-1]='0';
tank[center_x][center_y]='0';
tank[center_x][center_y+1]='0';
tank[center_x+1][center_y-1]='0';
tank[center_x+1][center_y+1]='0';
c1dir='H';
}
if(ch=='P')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x+1][center_y]='0';
tank[center_x][center_y-1]='0';
tank[center_x][center_y]='0';
tank[center_x][center_y+1]='0';
tank[center_x-1][center_y-1]='0';
tank[center_x-1][center_y+1]='0';
c1dir='P';
}
if(ch=='K')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x][center_y-1]='0';
tank[center_x-1][center_y]='0';
tank[center_x][center_y]='0';
tank[center_x+1][center_y]='0';
tank[center_x-1][center_y+1]='0';
tank[center_x+1][center_y+1]='0';
c1dir='K';
}
if(ch=='M')
{
int i=center_x-1,j;
for(;i<=center_x+1;i++)
for(j=center_y-1;j<=center_y+1;j++)
tank[i][j]=0;
tank[center_x][center_y+1]='0';
tank[center_x-1][center_y]='0';
tank[center_x][center_y]='0';
tank[center_x+1][center_y]='0';
tank[center_x-1][center_y-1]='0';
tank[center_x+1][center_y-1]='0';
c1dir='M';
}
}
}

void print(char map[wide][length],char tank[wide][length],char bullet[wide][length],char ctank[wide][length],char cbullet[wide][length])
{
int i,j;
for(i=0;i<wide;i++)
{
for(j=0;j<length;j++)
{
if(tank[i][j]!=0)
cout<<tank[i][j];
else if(bullet[i][j]!=0)
cout<<"*";
else if(ctank[i][j]!=0)
cout<<"0";
else if(cbullet[i][j]!=0)
cout<<"o";
else
cout<<map[i][j];
}
cout<<endl;
}
}

void Late(){for (int i=0;i<=pow(15,6);++i);}

void change(char map[wide][length],char tank[wide][length],char bullet[wide][length])
{
char ch;
int k;
while(1)
{
ch=getdir();
if(kbhit())
makebullet(bullet,center_x,center_y);
run(ch,map,tank);
movebullet(bullet,ctank,cbullet);
makectank(ctank,tank);
getcdir();
crun(cdir,map,ctank,center_c1x,center_c1y);
movebullet(cbullet,tank,bullet);
system("cls");
print(map,tank,bullet,ctank,cbullet);
Late();
for(int i=0;i<wide;i++)
for(int j=0;j<length;j++)
if(tank[i][j]!=0)
k++;
if(k==0)
return;
k=0;
}
}

void result()
{
cout<<"游戏结束"<<endl;
cout<<"歼敌数:"<<n;
}

int main()
{
system("color e");
getch();
makemap(map);
maketank(tank);
change(map,tank,bullet);
result();
return 0;
}

运行

开始运行生成地图、一个敌军坦克、一个我方坦克。敌军坦克随机运动射击,玩家控制按方向键控制我方坦克,每移动一次射击一颗子弹。我方射击中敌方,歼敌数加一,地方坦克消失后随机地点再生成。反之游戏结束。

运行

结束

阅读全文
cookie与session

cookie与session

会话

在了解cookie与session有必要了解会话,二者便是会话中的数据存储方式。

什么是会话

会话可以简单的理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称为一个会话。

会话过程要解决的问题

每个用户与服务器进行交互的过程中,各自会有一些数据,程序要想办法保存每个用户的数据(保存在request或servletContext中的数据一旦有新的请求数据就会消失)。


定义

cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。

细节

  • 一个cookie只能标识一种信息,他至少含有一个标识该信息的名称(name)和设置值(value)
  • 一个web站点可以给一个web浏览器发送多个cookie,一个web浏览器也可以存储多个web站点提供的cookie
  • 浏览器一般只允许存放300个cookie,每个站点最多存放20个cookie,每个cookie的大小限制为4KB
  • 如果创建了一个cookie,并将它发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie
  • 注意,删除cookie时,path必须一致,否则不会删除

    cookie技术图

    cookie技术图

session

定义

session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用的浏览器创建一个独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务。

细节

  • 在web开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其他程序时,其他程序可以从用户的session中取出该用户的数据,为用户服务
  • session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象

    session技术图

    session技术图

session与cookie的主要区别

cookie是把用户的数据写给用户的浏览器。session技术把用户的数据写到用户独占的session中。
session和cookie技术图

阅读全文
浅谈AOP

浅谈AOP

AOP简介

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。

AOP(面向切面编程)

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Spring的AOP原理图


AOP编程术语

切面(Aspect)

切面泛指交叉业务逻辑。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。

连接点(JoinPoint)

连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。

切入点(Poincut)

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

目标对象(Target)

目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。 上 例 中 的StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。

通知(Advice)

通知表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。


AspectJ 对 AOP 的实现

AspectJ 中常用的五种通知类型:

(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知

AspectJ 的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?)

解释:

modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)

举例:

符号 意义
* 0至多个任意字符
.. 用在方法参数中,表示任意多个参数;用在包名后,表示当前包及子包路径
+ 用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类

execution(public * (..))
指定切入点为:任意公共方法。
execution(
set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service..(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service...(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“”,表示包、子包下的所有类。
execution(
..service..(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(
.service..(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(
.ISomeService.(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* ..ISomeService.(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.(..))
指定切入点为:IAccountService 接口中的任意方法。
execution(
com.xyz.service.IAccountService+.(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(
joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。
execution(* joke(String,)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型,如joke(String s1,String s2)和joke(String s1,double d2)都是,但joke(String s1,double d2,String s3)不是。
execution(
joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。


AspectJ基于注解的AOP实现–Maven

搭建号Maven环境

引入依赖
引入AOP约束

定义业务接口与实现类

1
2
3
4
5
6
7
public interface SomeService {
void doSome(String name,int age);
String doOther(String name , int age);
String doFirst(String name,int age);
void doSecond(String name,int age);
void doThird();
}
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
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String naem,int age) {
System.out.println("SomeServiceImpl的业务方法doSome");
}

@Override
public String doOther(String name, int age) {
System.out.println("SomeServiceImpl的业务方法doOther");
return "abcd";
}

@Override
public String doFirst(String name, int age) {
System.out.println("SomeServiceImpl的业务方法doFirst");
return "doFirst";
}

@Override
public void doSecond(String name, int age) {
System.out.println("SomeServiceImpl的业务方法doSecond");
int i =10/0;

}

@Override
public void doThird() {
System.out.println("SomeServiceImpl的业务方法doThird");
}
}

定义切面类

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
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/*
* 切面类:是用来给业务方法增强功能的
* @Aspect:是aspectj框架中的,表示当前类是切面类
* 位置:在类的定义上面使用
* */
@Aspect
public class MyAspect {
//有功能增强的方法

/*
* 定义方法实现功能增强,方法的定义格式
* 1.public方法
* 2.一般都是void返回值(环绕通知除外)
* 3.方法名称自定义
* 4.方法可以有参数,参数的类型有限制
* */

/*
* @Before:前置通知
* 属性:value表示切入点表达式,表示切面执行的位置
* 位置:方法定义的上面
*
* 特点:1.在目标方法之前先执行的
* 2.不会影响目标方法的执行
* 3.不会改变目标方法的执行结果
* */
/*
* 获取doSome方法在执行时候的信息
* 参数:JoinPoint表示连接点(业务方法),
* 连接点是切入点中的一个方法
* */
@Before(value = "execution(* com.bjpowernode.service.SomeServiceImpl.doSome(..))")
public void myBefore(JoinPoint jp){
//在方法中,实现功能的增强,例如日志代码
System.out.println("前置通知:在目标方法之前,执行日志的功能");
//获取方法定义
System.out.println("连接点的方法定义:"+jp.getSignature());
System.out.println("连接点的方法名称:"+jp.getSignature().getName());
//获取方法执行时的参数
Object args [] = jp.getArgs();
for (Object arg : args) {
System.out.println(arg);
}
}

/*
* @AfterReturning:后置通知
* 属性:value:切入点表达式
* returning:自定义的变量,表示目标方法的放回值的
* 变量的名称必须和通知方法的参数名一样
* 位置:方法的上面
* 特点:
* 1.在目标方法之后执行的
* 2.能够获取到目标方法的执行结果
* 3.修改目标方法不会影响最后的执行结果
* */
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "result")
public void afterReturing(Object result){
//修改目标方法返回置
if(result != null){
String st = (String)result;
result = st.toUpperCase();
}
System.out.println("后置通知,在目标方法之后执行的。能够获取到目标方法的执行结果:"+result);
}

/*
* @Around:环绕通知
* 属性:value切入点表达式
* 位置:方法定义的上面
* 特点:
* 1.在目标方法的前和后都能增强功能
* 2.控制目标方法是否执行
* 3.修改目标方法的执行结果
*
* 环绕通知方法的定义:
* 1.参数proceedingJoinPoint
* 2.有返回值,推荐使用object
* */
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String name= "";
Object args [] = pjp.getArgs();
if(args.length>0){
name = (String)args[0];
}
Object result = null;
System.out.println("环绕通知:在目标方法之前加入日志");
//控制目标是否执行
if("zs".equals(name)){
//执行目标方法
result = pjp.proceed();//doFirst result = method.invoke(target,args);
}
System.out.println("环绕通知:在目标方法之后加入事务处理");
//返回目标方法的执行结果(可以是修改后的结果)
if(result != null){
result = "Hello Aspectj";
}
return result;
}

/*
* @AfterThrowing:异常通知
* 属性: value:切入点表达式
* throwing:自定义的变量,表示目标方法抛出异常现象,必须和通知方法的参数名一样
* 位置:方法定义的上面
* 特点:
* 1.在目标方法抛出异常时执行的,可以看作是对目标方法监控
* 2.不是异常处理程序,异常还是抛出
* */
//@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))", throwing = "ex")
@AfterThrowing(value = "mypt()",throwing = "ex")
public void myAfterThrowing(Throwable ex){
//能够获取异常信息
//1.把异常记录下来 2.发送通知
System.out.println("异常通知:在目标方法抛出异常时执行的,异常原因:"+ex.getMessage());
}

/*
* @After:最终通知
* 属性:value切入点表达式
* 位置:在方法的定义的上面
* 特点:
* 1.在目标方法之后执行
* 2.总是会被执行
* */
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myAfter(){
System.out.println("最终通知,总是会被被执行的,可以做收尾工作");
}

/*
* @Pointcut:定义和管理切入点
* 属性:value切入点表达式
* 位置:在自定义的方法上面
* 作用:@Pointcut定义在方法的上面,这个方法的名称就是切入点的别名
* 其他的通知注解的value属性可以使用方法名称,表示切入点
* */
@Pointcut(value = "execution(* *..SomeServiceImpl.doSecond(..))")
public void mypt(){
//无需代码
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--申明目标类对象-->
<bean id="SomeServiceTarget" class="com.bjpowernode.service.SomeServiceImpl"/>

<!--声明切面类对象-->
<bean id="myAspect" class="com.bjpowernode.aspect.MyAspect"/>

<!--声明自动代理生成器:使用aspectj把spring容器中目标类对象生成代理对象-->
<aop:aspectj-autoproxy/>

</beans>

测试类

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
public class AppTest {
@Test
public void test01(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

//从spring的容器中获取目标对象(代理对象)
SomeService proxy = (SomeService) ctx.getBean("SomeServiceTarget");

//通过代理对象执行业务方法,实现功能的增强
proxy.doSome("zhangsan",20);
}

@Test
public void test02(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

//从spring的容器中获取目标对象(代理对象)
SomeService proxy = (SomeService) ctx.getBean("SomeServiceTarget");

//通过代理对象执行业务方法,实现功能的增强
String str;
str = proxy.doOther("zs",21);
System.out.println("str:"+str);
}

@Test
public void test03(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

//从spring的容器中获取目标对象(代理对象)
SomeService proxy = (SomeService) ctx.getBean("SomeServiceTarget");

//通过代理对象执行业务方法,实现功能的增强
String str;
str = proxy.doFirst("zs",21);
System.out.println("str:"+str);
}

@Test
public void test04(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

//从spring的容器中获取目标对象(代理对象)
SomeService proxy = (SomeService) ctx.getBean("SomeServiceTarget");

//通过代理对象执行业务方法,实现功能的增强
proxy.doSecond("zs",21);
}

@Test
public void test05(){
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

//从spring的容器中获取目标对象(代理对象)
SomeService proxy = (SomeService) ctx.getBean("SomeServiceTarget");

//通过代理对象执行业务方法,实现功能的增强
proxy.doThird();
}
}

优点

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。
若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。

阅读全文
浅谈Spring ioc

IOC(控制反转)

解释

控制

控制对象的创建,属性的赋值,依赖关系的管理。以及对象从创建到销毁的整个生命周期。

反转

把开发人员在代码中创建的权限交给代码之外的Spring容器,Spring框架可以说是一个工厂,可以生产管理Java对象,当我们需要用到对象时可以直接从Spring容器中获取就行,不用再自己创建。


技术实现

DL

依赖查找。

DI

依赖注入,我们在代码中只需要提供对象的名称,对象的获取、赋值都由容器自己完成。若追究Spring框架的底层原理则是Java的反射机制。


Spring的IOC原理图

Spring的IOC原理图


举例

编写Java对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class School {
private String name;
private String address;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
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
public class Student {
private String name;
private int age;

//引用类型
private School school;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public School getSchool() {
return school;
}

public void setSchool(School school) {
System.out.println("setSchool:"+school);
this.school = school;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}

XML配置文件

1
2
3
4
5
6
7
8
9
10
<bean id="myStudent" class="com.zlt.bean02.Student">
<property name="name" value="李四"/><!--setNaem(李四)-->
<property name="age" value="20"/><!--setAge(20)-->
<property name="school" ref="myschool"/><!--setSchool()-->
</bean>
<!--声明School-->
<bean id="myschool" class="com.zlt.bean02.School">
<property name="name" value="北京大学"/>
<property name="address" value="北京"/>
</bean>

主类调用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class App {
public static void main(String[] args){
//定义spring配置文件
String config = "applicationContext.xml";
//创建Spring的容器对象,根据spring配置文件的位置,使用接口的不同实现类
//如果spring的配置文件是在类路径(classpath),使用ClassPathXmlApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//从容器中获取对象,使用getBean("<bean>的id")
SomeService student = (SomeService) ctx.getBean("myStudent");
//输出对象
System.out.println(student);
}
}

优点

Spring框架可以说是一个工厂,可以生产管理Java对象,当我们需要用到对象时可以直接从Spring容器中获取就行,不用再自己创建。以此也达到了解耦合的目的。

阅读全文
我的第一篇博客

写在前面

捣鼓了好几天,终于有了自己的博客。功能不是很完善,但是勉强还能看吧。感谢hexo上大佬的开源框架,以及b站上的大佬视频。

写博客的目的还是以学习为主。主要还是记录自己的学习心得,还有一些笔记和知识点。记录下来一是加强自己的记忆对知识点的二次记忆,二是当作笔记可以随时翻阅。当然分享出来也希望能和小伙伴们一起学习,有什么不足的地方也欢迎大家指正。

在过去的时间里也有学了一些东西,但是到了真正开始写博客的时候却有一种无从下笔的感觉。可能是之前的学习方法不太对,也可能是忘了一些。希望通过写博客可以将过去学过的知识捡回来一些,同时提高现在的学习效率。

文章也会不定时更新。最后祝大家新年快乐,心想事成,牛年大吉。

阅读全文