添加静态文件

第一种,使用property文件

1
2
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/

第二种,使用自定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* 静态资源映射
*/
@Component
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}

输入http://localhost:8080/static/java.png就可以访问了

注解的作用

本文打算介绍几个不太容易说出其区别,或者用途的 Spring 注解,比如 @Component 与 @Bean 的比较,@ControllerAdvice 是如何处理自定义异常的等等。

Spring 中的一些注解

1. @Component 和 @Bean 的区别是什么?

  1. 作用对象不同:@Component 注解作用于类,而 @Bean 注解作用于方法、
  2. @Component 通常是通过路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean 告诉了 Spring 这是某个类的实例,当我们需要用它的时候还给我。
  3. @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring 容器时,只能通过 @Bean 来实现。
1
2
3
4
5
6
7
8
9
@Bean //注解使用示例:
@Configuration public class AppConfig {
@Bean public TransferService transferService() { return new TransferServiceImpl(); } }
@Component //注解使用示例:
@Component public class ServiceImpl implements AService { .... }
//下面这个例子是通过
@Component
//无法实现的:
@Bean public OneService getService(status) { case (status) { when 1: return new serviceImpl1(); when 2: return new serviceImpl2(); when 3: return new serviceImpl3(); } }

2. Autowire 和 @Resource 的区别

  1. @Autowire 和 @Resource都可以用来装配bean,都可以用于字段或setter方法。Spring Boot 学习笔记分享给你。
  2. @Autowire 默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许 null 值,可以设置它的 required 属性为 false。
  3. @Resource 默认按名称装配,当找不到与名称匹配的 bean 时才按照类型进行装配。名称可以通过 name 属性指定,如果没有指定 name 属性,当注解写在字段上时,默认取字段名,当注解写在 setter 方法上时,默认取属性名进行装配。

注意:如果 name 属性一旦指定,就只会按照名称进行装配。
@Autowire和@Qualifier配合使用效果和@Resource一样:
@Autowired(required = false) @Qualifier(“example”) private Example example; @Resource(name = “example”) private Example example;
@Resource 装配顺序

  1. 如果同时指定 name 和 type,则从容器中查找唯一匹配的 bean 装配,找不到则抛出异常;
  2. 如果指定 name 属性,则从容器中查找名称匹配的 bean 装配,找不到则抛出异常;
  3. 如果指定 type 属性,则从容器中查找类型唯一匹配的 bean 装配,找不到或者找到多个抛出异常;
  4. 如果不指定,则自动按照 byName 方式装配,如果没有匹配,则回退一个原始类型进行匹配,如果匹配则自动装配。

3. 将一个类声明为 Spring 的 bean 的注解有哪些?

  • @Component :通用的注解,可标注任意类为 Spring 的组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。
  • @Repository :对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service :对应服务层,主要设计一些复杂的逻辑,需要用到 Dao 层。
  • @Controller :对应 Spring MVC 控制层,主要用来接受用户请求并调用 Service 层返回数据给前端页面。
  • @Configuration :声明该类为一个配置类,可以在此类中声明一个或多个 @Bean 方法。

4. @Configuration :配置类注解

@Configuration 表明在一个类里可以声明一个或多个 @Bean 方法,并且可以由 Spring 容器处理,以便在运行时为这些 bean 生成 bean 定义和服务请求,例如:

1
@Configuration public class AppConfig {     @Bean public MyBean myBean() { // instantiate, configure and return bean ...     } }

我们可以通过 AnnotationConfigApplicationContext 来注册 @Configuration 类:

1
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class); ctx.refresh(); MyBean myBean = ctx.getBean(MyBean.class); // use myBean ...

另外也可以通过组件扫描(component scanning)来加载,@Configuration 使用 @Component 进行原注解,因此 @Configuration 类也可以被组件扫描到(特别是使用 XML 的 元素)。@Configuration 类不仅可以使用组件扫描进行引导,还可以使用 @ComponentScan 注解自行配置组件扫描:
@Configuration @ComponentScan(“com.acme.app.services”) public class AppConfig { // various @Bean definitions … }
使用 @Configuration 的约束:

  • 配置类必须以类的方式提供(比如不能是由工厂方法返回的实例)。
  • 配置类必须是非 final 的。
  • 配置类必须是非本地的(即可能不在方法中声明),native 标注的方法。
  • 任何嵌套的配置类必须声明为 static。
  • @Bean 方法可能不会反过来创建更多的配置类。

除了单独使用 @Configuration 注解,我们还可以结合一些外部的 bean 或者注解共同使用,比如 Environment API,@PropertySource,@Value,@Profile 等等许多,这里就不做详细介绍了,更多的用法可以参看 Spring @Configuration 的相关文档 。
推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice

5. @ControllerAdvice :处理全局异常利器

在 Spring 3.2 中,新增了 @ControllerAdvice、@RestControllerAdvice、@RestController 注解,可以用于定义 @ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有 @RequestMapping 、@PostMapping、@GetMapping等这些 Controller 层的注解中。
默认情况下,@ControllerAdvice 中的方法应用于全局所有的 Controller。而使用选择器 annotations(),basePackageClasses() 和 basePackages() (或其别名value())来定义更小范围的目标 Controller 子集。Spring Boot 学习笔记分享给你。
如果声明了多个选择器,则应用 OR 逻辑,这意味着所选的控制器应匹配至少一个选择器。请注意,选择器检查是在运行时执行的,因此添加许多选择器可能会对性能产生负面影响并增加复杂性。
@ControllerAdvice 我们最常使用的是结合 @ExceptionHandler 用于全局异常的处理。可以结合以下例子,我们可以捕获自定义的异常进行处理,并且可以自定义状态码返回:

1
2
3
4
5
6
7
8
9
10
@ControllerAdvice("com.developlee.errorhandle") public class MyExceptionHandler {     /**      * 捕获CustomException      * @param e      * @return json格式类型      */     
@ResponseBody
@ExceptionHandler({CustomException.class}) //指定拦截异常的类型

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //自定义浏览器返回状态码
public Map>String, Object< customExceptionHandler(CustomException e) {
Map<String, Object> map = new HashMap <>();
map.put("code", e.getCode());
map.put("msg", e.getMsg()); return map;
} }

更多信息可以参看 Spring @ControllerAdvice 的官方文档。推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice

6. @Component, @Repository, @Service 的区别

image.png
@Component是一个通用的Spring容器管理的单例bean组件。而@Repository, @Service, @Controller就是针对不同的使用场景所采取的特定功能化的注解组件。
因此,当你的一个类被@Component所注解,那么就意味着同样可以用@Repository, @Service, @Controller 来替代它,同时这些注解会具备有更多的功能,而且功能各异。
最后,如果你不知道要在项目的业务层采用@Service还是@Component注解。那么,@Service是一个更好的选择。

常用注解解析

1. 有这么一个故事,从xml配置文件的bean说起

Spring用xml配置文件的时候(不知道阅读这篇文章的你用没用过,我用过一段时间,那是黑暗伤痛的回忆QQQ),一个xml配置文件里面有很多个bean。类似这样:

1
2
3
<bean id="helloWorld" class="com.test.spring.beans.HelloWorld">
<property name="name" value="Spring"></property>
</bean>

每个bean都对应着一个class,可能是controller,可能是service,可能是dao,xml配置文件就是告诉Spring,我这里有哪些bean,他们都叫啥名字(例如helloworld),他们的class文件在哪(com.test.spring.beans.HelloWorld),他们都有哪些属性。这样,当项目启动时,Spring就回去管理这些类了。

2. 进入@Configuration时代

你现在使用@Service@Repository@Component这些注解放在java的类上用来告诉Spring:“我标注的这些类请你来管理”。在以前就是要在xml配置文件里写上面这样的bean,有一个类写一个bean(实在是太麻烦了。。。。。,不方便码代码,不方便阅读,不方便修改),一个xml配置文件写好多bean。
总结一下:

  • @Configuration注解就相当于xml配置文件,告诉Spring我这里有好多bean要交给你管理
  • @Bean注解就相当于xml配置文件里面的bean,告诉Spring这个被注解的类就交给你管理了
    到这里,Spring发展史中注解替换xml配置文件的故事就讲完了,是不是很简单!

=十万个为什么的分割线=======
那,既然问题都解决了,有@Component啥事???
年轻人,sometimes naive

3. @Component粉墨登场

无名子曰过,偷懒是人类创造力的源泉。我都把class类写好了,还要再写个@Configuration注解的class去告诉Spring(除了通知Spring,这个class不产生其他价值),虽然这个class相比于xml配置文件写起来方便又好阅读,但是,这个时候总有但是,我为啥不直接就告诉Spring呢???
  @Controller@Service@Repository都是@Component的更具体一点的实现(这里如果说错了,求大佬轻喷)。写完class加上这些注解,其实就是加上@Component注解,Spring就懂了,奥奥,你小子太懒了,这么多类全部是让我来管理的,不过话说回来谁让我是你baba呢!
  是不是这就完事了呢,是的,没有但是,真的就完事了。

4. “但是”又回来了

是的,这里又有但是了。可能有人想到了,既然@Component跟Spring已经把问题解决了,那,@Bean这个注解为啥还没退休啊,还在这站着地儿,咋不见新版Spring给加个@Deprecated???
  年轻人,有想法!!!
  假设这么一种情况,有几个class,我自己也不是不能写,但是写了周末就没时间去外滩闲逛了,就在这个时候,我发现alimama的老铁们已经写好了同样功能的class,我在maven里import一下就完了,开心三秒,又有问题了,他们写的时候没用Spring:

  • 1)打好的jar包,我不能在class上面加@Component注解;
  • 2)我没有他们的源码,也不能把他们的代码复制过来,假装自己重写了一遍QQQ

这时候@Bean的作用就体现出来了,请看:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class MyClass {
// class1和class2就是jar包里写好的
@Bean
public Class1 getClass1() {
return new Class1();
}
@Bean
public Class2 getClass1() {
return new Class2();
}
.....
}

虽然又回到了@Component出场之前的@Configuration时代。但是你也不需要经常使用这个对吧

4. 让我们以一点小干货结束今天的故事

Spring管理的Bean我们需要通过@Autowired或者@Resource导入来使用,这两的区别啥的你可以自己去搜索一下,这里只说一个问题。@Autowired是按照类型装配的,@Resource是按照名称装配的,加入同一类型有多个bean,只是名字不一样,@Autowired直接导入会报错。这时候课题通过@Resource(name="name")或者@Autowired@Qualifier("name")来按名称装配,解决问题。

java-jwt使用

单点登录
https://github.com/longguikeji/arkid

JwtUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
package com.macro.mall.tiny.security.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
* JwtToken生成的工具类
* JWT token的格式:header.payload.signature
* header的格式(算法、token的类型):
* {"alg": "HS512","typ": "JWT"}
* payload的格式(用户名、创建时间、生成时间):
* {"sub":"wang","created":1489079981393,"exp":1489684781}
* signature的生成算法:
* HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
* Created by macro on 2018/4/26.
*/
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Value("${jwt.tokenHead}")
private String tokenHead;

/**
* 根据负责生成JWT的token
*/
private String generateToken(Map<String, Object> claims) {
Algorithm algorithm = Algorithm.HMAC512(secret);
System.out.println("生成的过期时间");
System.out.println(generateExpirationDate());
return JWT.create()
.withClaim(CLAIM_KEY_USERNAME, String.valueOf(claims.get(CLAIM_KEY_USERNAME)))
.withClaim(CLAIM_KEY_CREATED, String.valueOf(claims.get(CLAIM_KEY_CREATED)))
.withExpiresAt(generateExpirationDate())
.sign(algorithm);

}

/**
* 从token中获取JWT中的负载
*/
private Map<String, Claim> getClaimsFromToken(String token) {
Map<String, Claim> claims = new HashMap<>(2);
Algorithm algorithm = Algorithm.HMAC512(secret);
JWTVerifier verifier = JWT.require(algorithm)

.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
try {
claims = jwt.getClaims();


} catch (Exception e) {
LOGGER.info("JWT格式验证失败:{}", token);
}
return claims;
}

/**
* 生成token的过期时间
*/
private Timestamp generateExpirationDate() {
return new Timestamp(System.currentTimeMillis() + expiration * 1000);
}

/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
String username;
try {
Map<String, Claim> claims = getClaimsFromToken(token);
System.out.println(claims);
username = claims.get(CLAIM_KEY_USERNAME).asString();
} catch (Exception e) {
username = null;
}
return username;
}

/**
* 验证token是否还有效
*
* @param token 客户端传入的token
* @param userDetails 从数据库中查询出来的用户信息
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}

/**
* 判断token是否已经失效
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
System.out.println("过期了吗");
System.out.println(expiredDate);
System.out.println(new Date());
return expiredDate.before(new Date());
}

/**
* 从token中获取过期时间
*/
private Date getExpiredDateFromToken(String token) {
Algorithm algorithm = Algorithm.HMAC512(secret);
JWTVerifier verifier = JWT.require(algorithm)

.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);

return jwt.getExpiresAt() ;

}

/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>(2);
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, System.currentTimeMillis());
return generateToken(claims);
}

/**
* 当原来的token没过期时是可以刷新的
*
* @param oldToken 带tokenHead的token
*/
public String refreshHeadToken(String oldToken) {
if (StrUtil.isEmpty(oldToken)) {
return null;
}
String token = oldToken.substring(tokenHead.length());
if (StrUtil.isEmpty(token)) {
return null;
}
//token校验不通过
Map<String, Claim> claims = getClaimsFromToken(token);
if (claims == null) {
return null;
}
//如果token已经过期,不支持刷新
if (isTokenExpired(token)) {
return null;
}
Map<String, Object> claim = new HashMap<>(2);
//如果token在30分钟之内刚刷新过,返回原token
if (tokenRefreshJustBefore(token, 30 * 60)) {
return token;
} else {
claim.put(CLAIM_KEY_USERNAME, claims.get(CLAIM_KEY_USERNAME).asString());
claim.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claim);
}
}

/**
* 判断token在指定时间内是否刚刚刷新过
*
* @param token 原token
* @param time 指定时间(秒)
*/
private boolean tokenRefreshJustBefore(String token, int time) {
Map<String, Claim> claims = getClaimsFromToken(token);
Date created = claims.get(CLAIM_KEY_CREATED).asDate();
Date refreshDate = new Date();
//刷新时间在创建时间的指定时间内
if (refreshDate.after(created) && refreshDate.before(DateUtil.offsetSecond(created, time))) {
return true;
}
return false;
}
}

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.example.first.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.first.model.User;
import com.example.first.service.UserService;
import com.example.first.utils.JwtUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;

/**
* @author Yangzhengqian
* @description
* @date:Created time 2021/8/11 15:02
* @modified By:
*/
@RestController
public class UserController {
@Resource
UserService userService;

@PostMapping("/login")
public HashMap login(String username, String password) {
User sqlUser = userService.getOne(new QueryWrapper<User>().eq("username", username));
String token = "";
HashMap h = new HashMap();

if (Objects.equals(password, sqlUser.getPassword())) {
token = JwtUtil.sign(username, password);
System.out.println(token);
h.put("token", token);
return h;
}


return h;
}

@PostMapping("/reg")
public String reg(String username, String password) {
User u = new User();
u.setUsername(username);
u.setPassword(password);
userService.save(u);
return u.toString();
}
@GetMapping("/ver")
public HashMap ver(@RequestParam("token") String token){
//String username=JwtUtil.getUsername(token);
Map user=JwtUtil.getTokenInfo(token);
HashMap<String,String> res=new HashMap<>(1);
res.put("user", String.valueOf(user));
return res;
}

@PostMapping ("/uploadImg")
public HashMap uploadFile(@RequestParam("image") MultipartFile image) {
String imgurl="";
HashMap<String, Object> res=new HashMap<>();
//存入数据库的文件地址集合
List<String> pathList = new ArrayList<>();
//文件上传
if (!image.isEmpty()) {
try {
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
//本地测试上传图片文件夹名称
String fileName =image.getOriginalFilename();
// 文件名称
String suffix = fileName.substring(fileName.lastIndexOf('.'));
// 新文件名(唯一)
String newFileName = "C:\\Users\\yzqde\\tmp\\"+System.currentTimeMillis() + suffix;

File newFile = new File(newFileName);
if (!newFile.exists()) {
newFile.createNewFile();
}
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
out.write(image.getBytes());
out.flush();
out.close();
//此处将imgurl路径进行变更

pathList.add(newFileName);
res.put("path",pathList);
return res;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}

初始化执行操作

通常的我们的项目开发中,经常会遇到那种在服务一启动就需要自动执行一些业务代码的情况。比如将数据库中的配置信息或者数据字典之类的缓存到redis,或者在服务启动的时候将一些配置化的定时任务开起来。关于spring mvc或者springboot如何在项目启动的时候就执行一些代码

@PostConstruct注解

从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。@PostConstruct会在所在类的构造函数执行之后执行,在init()方法执行之前执行。(@PreDestroy注解的方法会在这个类的destory()方法执行之后执行。)
@PostConstruct
不是spring提供的而是Java自己的注解。

Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct注释用于需要依赖注入完成后才能执行任何初始化的方法上。 在类投入使用之前,必须调用此方法

在使用spring框架时,在一个类内,如果有构造器(Constructor ),有@PostConstruct,还有@Autowired,他们的先后执行顺序为Constructor >> @Autowired >> @PostConstruct。在类加载的时候,为当前类初始化一些数据,那么可以使用@PostConstruct注解

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
@Component // 注意 这里必须有
public class StartAllJobInit {

protected Logger logger = LoggerFactory.getLogger(getClass().getName());
@Autowired
JobInfoService jobInfoService;

@Autowired
JobTaskUtil jobTaskUtil;

@PostConstruct // 构造函数之后执行
public void init(){
System.out.println("容器启动后执行");
startJob();
}

public void startJob() {
List<JobInfoBO> list = jobInfoService.findList();
for (JobInfoBO jobinfo :list) {
try {
if("0".equals(jobinfo.getStartWithrun())){
logger.info("任务{}未设置自动启动。", jobinfo.getJobName());
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
}else{
logger.info("任务{}设置了自动启动。", jobinfo.getJobName());
jobTaskUtil.addOrUpdateJob(jobinfo);
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING);
}
} catch (SchedulerException e) {
logger.error("执行定时任务出错,任务名称 {} ", jobinfo.getJobName());
}
}
}
}

实现CommandLineRunner接口并重写run()方法

其实有两个接口ApplicationRunner,CommandLineRunner

这两个接口中有一个run方法,只需要实现这个方法即可。这两个接口的不同之处在于:ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组。
:::tip
注意:一定要有@Component这个注解。要不然SpringBoot扫描不到这个类,* 是不会执行。

@Order注解
如果有多个实现类,而需要他们按一定顺序执行的话,可以在实现类上加上@Order注解。@Order(value=整数值)。SpringBoot会按照@Order中的value值从小到大依次执行。
:::
例子

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
@Component // 注意 这里必须有
//@Order(2) 如果有多个类需要启动后执行 order注解中的值为启动的顺序
public class StartAllJobInit implements CommandLineRunner {

protected Logger logger = LoggerFactory.getLogger(getClass().getName());
@Autowired
JobInfoService jobInfoService;

@Autowired
JobTaskUtil jobTaskUtil;

@Override
public void run(String... args) {
List<JobInfoBO> list = jobInfoService.findList();
for (JobInfoBO jobinfo :list) {
try {
if("0".equals(jobinfo.getStartWithrun())){
logger.info("任务{}未设置自动启动。", jobinfo.getJobName());
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
}else{
logger.info("任务{}设置了自动启动。", jobinfo.getJobName());
jobTaskUtil.addOrUpdateJob(jobinfo);
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING);
}
} catch (SchedulerException e) {
logger.error("执行定时任务出错,任务名称 {} ", jobinfo.getJobName());
}
}
}
}

使用监听器

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



/**
* 初始化常量的监听器
* <p>
* 当spring装配好配置后,就去数据库读constants
* <p>等待解决</p>
* <p>
* <a href="https://docs.spring.io/spring-boot/docs/2.7.0/reference/htmlsingle/#features.spring-application.application-events-and-listeners">链接</a>
* </p>
*
* @author yanni
*/

public class ConstantsInitListener implements ApplicationListener<ApplicationContextInitializedEvent>, Ordered {

private static final Log log = Log.get();

private static final String initSql = "select config_value from tb_blog_config where config_field = 'init'";

@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}

@Override
public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {
ConfigurableEnvironment environment = applicationContextInitializedEvent.getApplicationContext().getEnvironment();

// 获取数据库连接配置
String dataSourceUrl = environment.getProperty("spring.datasource.url");
String dataSourceUsername = environment.getProperty("spring.datasource.username");
String dataSourcePassword = environment.getProperty("spring.datasource.password");

// 如果有为空的配置,终止执行
if (ObjectUtil.hasEmpty(dataSourceUrl, dataSourceUsername, dataSourcePassword)) {
try {
throw new Exception("database not cononnected");
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Connection conn = null;
try {
Class.forName("org.postgresql.Driver");
assert dataSourceUrl != null;
conn = DriverManager.getConnection(dataSourceUrl, dataSourceUsername, dataSourcePassword);
Entity init = SqlExecutor.query(conn, initSql, new EntityHandler());
if (init == null) {
SqlExecutor.execute(conn,SqlConstant.initAdminSql );
SqlExecutor.execute(conn, SqlConstant.insertConfigDataSql,new Object() );
SqlExecutor.execute(conn,SqlConstant.initTagSql );
SqlExecutor.execute(conn,SqlConstant.initCateSql );
SqlExecutor.execute(conn,SqlConstant.initLinkSql );
log.info("执行sql成功!");
} else {
log.info("数据已经存在");
}
// 获取sys_config表的数据

} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
log.error(">>> 读取数据库constants配置信息出错:{}", e.getMessage());

} finally {
DbUtil.close(conn);
}

}
}

然后在resources/META-INF/spring.factories写下

1
2
org.springframework.context.ApplicationListener=\
com.site.blog.config.listener.ConstantsInitListener

就可以在启动前写入sql

监听器

springboot使用controller传递参数

:::tip

see https://github.com/yzqdev/spring-tutor/tree/dev/spring-transfer
额外内容 AntPathMatcher和PathPattern的区别
详细文档见spring.io
:::

基本传参方式

直接上代码

:::tip
注意
multipart/form-data与x-www-form-urlencoded的区别:
multipart/form-data:可以上传文件或者键值对,最后都会转化为一条消息
x-www-form-urlencoded:只能上传键值对,而且键值对都是通过&间隔分开的。
application/json: 上传的是json键值对
:::

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
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {
private final IUserService userService;

/**
* 获取用户
* api使用: http://localhost:9400/user/getUser
* @return {@link User}
*/
@GetMapping("user")
public User getUser() {
return null;
}

/**
* 使用pathvariable
* api使用: http://localhost:9400/userByPath/abcdde
*
* @param id id
* @return {@link String}
*/
@GetMapping("userByPath/{id}")
public String getUserByPath(@PathVariable("id") String id) {
return id;
}
/**
* 添加用户
* api使用: http://localhost:9400/user/addUserstr?username=aa&password=bb
* @return {@link User}
*/
@PostMapping("/addUser")
public User addUser(User user) {
return user;
}

/**
* 添加用户
* api使用: http://localhost:9400/user/addUserbody
*<pre>
* <code>
* {
* "username": "aa",
* "password": "bb"
* }
* </code>
*</pre>
* @param user 用户
* @return {@link User}
*/
@PostMapping("/addUserBody")
public User addUserBody(@RequestBody User user) {
return user;
}


/**
* 添加用户
*api使用: http://localhost:9400/user/addUserstr?username=aa&password=bb
* @param username 用户名
* @param password 密码
* @return {@link User}
*/
@PostMapping("/addUserstr")
public User addUserString(String username, String password) {
User user = User.builder().username(username).password(password).build();
return user;
}

/**
* 删除多个用户
* api使用: http://localhost:9400/user/deleteUsers
* <pre>
* <code>
* {
* "userIds": [
* "string"
* ]
* }
* </code>
* </pre>
* @param userDelDto 用户del dto
* @return {@link HashMap}<{@link String}, {@link Object}>
*/
@DeleteMapping("/deleteUsers")
public HashMap<String, Object> deleteUsers(@RequestBody UserDelDto userDelDto) {
HashMap<String, Object> res = new HashMap<>();
res.put("obj", userDelDto);

return res;
}

/**
* 删除users1
* api使用: http://localhost:9400/user/deleteUsers1
*[
* "string"
* ]
* @param ids id
* @return {@link HashMap}<{@link String}, {@link Object}>
*/
@DeleteMapping("/deleteUsers1")
public HashMap<String, Object> deleteUsers1(@RequestBody String[] ids) {
HashMap<String, Object> res = new HashMap<>();
res.put("obj", ids);

return res;
}

/**
* 获取请求头信息
*
* @return {@link HashMap}<{@link String}, {@link Object}>
*/
@GetMapping("/users")
public HashMap<String, Object> getUsers() {
HashMap<String, Object> res = new HashMap<>();
String token = RequestHelper.getRequestHeader("token");
String auth = RequestHelper.getRequestHeader("Authorization");
res.put("token", token);
res.put("auth", auth);
return res;
}

/**
* 检索
*api使用: http://localhost:9400/user/retrieve?username=aa
* @param username 用户名
* @return {@link User}
*/
@GetMapping("/retrieve")
public User retrieve(@RequestParam("username") String username) {
return userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
}

}

map传参方式(不推荐)

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
@RestController
@RequestMapping("/my")
public class MyController {

/**
* 保存
* 用map接收
* api使用: http://localhost:9400/my/save
* <pre>
* <code>
* {
* "additionalProp1": "string",
* "additionalProp2": "string",
* "additionalProp3": "string"
* }
* </code>
* </pre>
* @param map 地图
* @return {@link Map}<{@link String}, {@link String}>
*/
@PostMapping("/save")
public Map<String, String> save(@RequestBody Map<String,String> map) {
return map;
}
}

0%