线程三种创建方式
①继承Thread类,重写run()方法(不能抛出异常),单继承,不能继承其他类,所以资源不能共享;
②实现Runnable接口,重写run()方法,可以继承其他类,进而共享同一个目标对象;
③实现Callable接口,重写call()方法(可以抛出异常),有返回值,通过callable任务可以拿到Future对象,获得计算结果。
其中,start()方法用来开启线程,但不会立马结束,会进入可执行状态,需要获取到CPU使用权才会执行;run()方法是由jvm创建,本地操作系统回调的方法,不能手动调用。
lambda 表达式使用
List<User> list = Arrays.asList(
new User((long) 100,"zhangsan",3,"qwe"),
new User((long) 104,"zhangwu",3,"qwe"),
new User((long) 101,"lisi",4,"asdaf"));
//输出用户集合中名字以“z"开头的名字
list.stream()
.filter(p->p.getName().startsWith("z"))
.forEach(p-> System.out.println(p.getName()));
User user = new User((long)1,"zhangyi",1,null);
Optional<User> userOption = Optional.ofNullable(user);
//夺命连环null检查
System.out.println(userOption.map(o->o.getName())
.map(n->n.toUpperCase())
.orElse(null));
查看依赖包之间的关系
执行以下命令
mvn dependency:tree
spring boot使用定时任务锁
引入定时任务锁依赖
<!-- shedlock定时任务锁 -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>4.30.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>4.30.0</version>
创建表结构(一定要使用改表名字和字段,源码已封装)
CREATE TABLE `shedlock` (
`name` varchar(64) NOT NULL,
`lock_until` timestamp(3) NULL DEFAULT NULL,
`locked_at` timestamp(3) NULL DEFAULT NULL,
`locked_by` varchar(255) DEFAULT NULL,
PRIMARY KEY (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='shedlock分布式定时任务表';
启动配置类上加上如下注解
@EnableSchedulerLock(defaultLockAtMostFor = "PT60S")//定时任务锁,设置默认时间60S
@EnableScheduling // 开启定时功能的注解
编写配置类
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@Component
public class ShedLockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
编写定时任务
@Component
@Slf4j
public class remindJob {
@Autowired
RemindService remindService;
/**
* 每天执行一次,每天晚上12点
*/
@Scheduled(cron = "0 0 0 * * *")
@SchedulerLock(name = "remindJob", lockAtMostForString = "PT60S", lockAtLeastForString = "PT60S")
public void dingShi() {
LocalDateTime localDateTime = LocalDateTime.now();
XxlJobLogger.log("开始搜寻 RemindService. 今日:" + localDateTime);
log.info("[RemindJobHandler] 定时job开始");
Tip tip = remindService.getRemindJob();
log.info("[RemindJobHandler] 完成条数 tip = {}", JSONUtils.toJSONString(tip));
}
name:定时任务的名字,就是数据库中的内个主键
lockAtMostFor:锁的最大时间单位为毫秒
lockAtMostForString:最大时间的字符串形式,例如:PT60S 代表60秒
lockAtLeastFor:锁的最小时间单位为毫秒
lockAtLeastForString:最小时间的字符串形式
时间对比
获取明日0时0分0秒
LocalDate localDate = LocalDate.now();
// 当前日期+1
localDate = localDate.plusDays(1);
LocalDateTime dateTime = LocalDateTime.of(localDate.getYear(), localDate.getMonth(), localDate.getDayOfMonth(), 0, 0, 0);
时间比大小
现在的时间是否大于dateTime,是返回true,不是返回false
LocalDateTime.now().isAfter(dateTime)
拿数据库的时间来对比
只获取今天的时间
QueryWrapper<Remind> queryWrapper = new QueryWrapper<>();
//只显示今日的提醒
LocalDate localDate = LocalDate.now();
LocalDateTime jintian = LocalDateTime.of(localDate.getYear(),localDate.getMonth(),localDate.getDayOfMonth(),0,0,0);
localDate = localDate.plusDays(1);
LocalDateTime mingtian = LocalDateTime.of(localDate.getYear(),localDate.getMonth(),localDate.getDayOfMonth(),0,0,0);
queryWrapper.between(Remind.REMIND_DATE,jintian,mingtian);
配置跨域
package com.guli.getway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Configuration //gateway
public class corsConfig {
@Bean//添加过滤器
public CorsWebFilter corsWebFilter(){
// 基于url跨域,选择reactive包下的,不然return会报错
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 跨域配置信息
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许跨域的头
corsConfiguration.addAllowedHeader("*");
// 允许跨域的请求方式
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求来源
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie跨域
corsConfiguration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
配置阿里云存储对象OOS
添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alicloud-oss</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
yaml配置OOS存储对象信息
具体信息需要登录阿里云存储对象管制台和AccessKey管理查看具体信息
spring:
cloud:
alicloud:
access-key: LTAI5tFAKaXnDVsNqZmoTL #阿里云云账号密钥
secret-key: DNNc4sxqbd2lkimKZX5VElwyf6yZ #阿里云云账号密钥
oss:
endpoint: https://oss-cn-guanhou.aliyuncs.com #存储对象的域名地址
代码
通过签名和秘钥给前端上传数据让前端直接上传文件到存储对象OOS
package com.guli.disanfang.controller;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
@RestController
@RequestMapping("third-service/oss")
public class OSSController {
@Autowired
private OSS ossClient;
@Value("${spring.cloud.alicloud.oss.bucket}")
private String bucket;
@Value("${spring.cloud.alicloud.oss.endpoint}")
private String endpoint;
@Value("${spring.cloud.alicloud.access-key}")
private String accessId;
private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@GetMapping("/policy")
public Map<String, String> getPolicy(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
// callbackUrl为上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
// String callbackUrl = "http://88.88.88.88:8888";
String dir = format.format(new Date())+"/"; // 用户上传文件时指定的前缀。以日期格式存储
// 创建OSSClient实例。
Map<String, String> respMap= null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
// PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);//生成协议秘钥
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);//生成的协议秘钥
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
// respMap.put("expire", formatISO8601Date(expiration));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
ossClient.shutdown();
}
return respMap;
}
}
JSR303数据校验
引入依赖
如果springboot有引入就不用引入了
<!--jsr3参数校验器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
实体类参数校验
controller
/**
* 保存
*/
@RequestMapping("/save")
//@Valid打开校验规则
public R save(@Valid @RequestBody PmsBrandEntity pmsBrand){
// List<FieldError> fieldErrors = bindingResult.getFieldErrors();
// if (!StringUtils.isEmpty(fieldErrors)){
// Map<String, String> map = new HashMap<>();
// fieldErrors.forEach(item->{
// map.put(item.getField(), item.getDefaultMessage());
// });
// return R.error(400,"参数异常!").put("error",map);
// }
pmsBrandService.save(pmsBrand);
return R.ok();
}
分组校验参数
添加校验接口(controller判断是哪个校验接口,分组对哪个接口生效)
//更新校验
public interface UpdateVail {}
//新增校验
public interface AddVail {}
参数校验
在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups
@NotNull(message = "修改必须定制品牌id", groups = {UpdateVailGroup.class})
@Null(message = "新增不能指定id", groups = {AddVailGroup.class})
private Long brandId;
业务方法参数上使用@Validated注解,并用@Validated指定使用校验的接口标识
统一异常
@RestControllerAdvice(basePackages = "com.guli.shangpin.controller" )
@Slf4j
public class conExceptionAdv {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R conException(MethodArgumentNotValidException e){
Map<String,String> map = new HashMap<>();
e.getBindingResult().getFieldErrors().forEach(item->{
map.put(item.getField(), item.getDefaultMessage());
});
log.error("消息提示{},类型{}",e.getMessage(),e.getClass());
return R.error(CodeMessage.CANSHU_ERROR.getCode(),CodeMessage.CANSHU_ERROR.getMessage()).put("data",map);
}
}
异常枚举
public enum CodeMessage {
CANSHU_ERROR(10001,"参数异常!"),
WEIZHI_ERROR(10000,"未知错误!");
private int code;
private String message;
CodeMessage(int code, String message){
this.code = code;
this.message=message;
}
public int getCode(){
return code;
}
public String getMessage(){
return message;
}
}