老规矩,先讲技术后说故事~
开发会在 target文件夹下classes/static
打jar包后会在jar同级目录下static
// java
File path = new File(ResourceUtils.getURL("classpath:").getPath());
File upload = new File(path.getAbsolutePath(),strBuilder("static/upload/",type,"/"));
if(!upload.exists()) {
return upload.mkdirs()?upload.getPath()+"/":null;
}else
return upload.getPath()+"/";
# yml
resource:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:static/
# application.yml
kcVideo:
upload: E:/static/
@Configuration
public class StaticResourceConfig extends WebMvcConfigurerAdapter {
@Value("${kcVideo.upload}")
private String uploadPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 处理配置文件中的配置项,规范配置值最后的'/'
StringBuilder path = new StringBuilder(uploadPath);
int lastCharIndex = path.length();
char lastChar = path.charAt(lastCharIndex-1);
if (lastChar == '\\')
path.setCharAt(lastCharIndex-1,'/');
else if (lastChar != '/')
path.append('/');
uploadPath = path.toString();
//针对'/static/**'请求,添加静态文件夹
registry.addResourceHandler("/static/**").addResourceLocations("file:"+uploadPath);
//配置文件上传文件夹
//FileHelper的代码就不贴了
FileHelper.uploadPath = uploadPath;
super.addResourceHandlers(registry);
}
}
配置拦截器时,体会到了1.x版本和2.x版本的差异性。拦截路径通过写白名单的方式来实现。下面的配置基于2.x版本。
白名单:
# application.yml
spring:
profiles:
include: whiteList
# application-whiteList.yml
qaq:
jwt-path:
whiteList:
- /qaqManager/**
- /index
拦截器:
拦截器可以实现父类中的三个方法及执行顺序:
拦截器preHandle执行
Controller执行
拦截器postHandle执行
View视图渲染
拦截器afterCompletion执行
关于afterConcurrentHandlingStarted方法,是当Controller中有异步请求执行时调用,具体效果也没有试过了。
// UserTokenInterceptor.java
public class UserTokenInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
response.setContentType("application/json;charset=utf-8");
response.setCharacterEncoding("UTF-8");
if (token == null){
JSONObject jsonObject = new JSONObject(ResponseBuilder.noTokenError());
response.getWriter().write(jsonObject.toJSONString());
return false;
} else {
return TokenUtil.parseToken(token);
}
}
}
配置拦截器:
这里有个注意点,注入参数时用的是@ConfigurationProperties注解,@Value注解并不支持复杂数据结构参数的注入。
@Data
@Configuration
@ConfigurationProperties("qaq.jwt-path")
public class QaqWebMvcConfigurer implements WebMvcConfigurer {
private List<String> whiteList;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserTokenInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(whiteList);
registry.addInterceptor(new ManagerTokenInterceptor())
.addPathPatterns("/qaqManager/**")
.excludePathPatterns("/qaqManager/login");
}
}
关于配置拦截器,版本间我们可以做个对比:
1.x:
2.x:
由于spring boot 自带是jackson,其与fastjson对比下在一些应用场景下的性能会差上一些,也可以说是fastjson在json编解码转化方面的性能太强了。所以我们通过手动配置用fastjson替换掉jackson。
首先在配置类中打断点,看一下内部原有配置:
现在把原有配置中的jaskson替换:
// 先写配置类
// QaqWebMvcConfigurer.java
@Configuration
public class QaqWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
// 配置字符集
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
// 配置FastJson相关配置项
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat //优化json格式的可读性
);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
// 替换Jackson
for (int i = 0;i < converters.size();i ++){
if (converters.get(i) instanceof MappingJackson2HttpMessageConverter)
converters.set(i,fastJsonHttpMessageConverter);
}
}
}
对fastjson特有注解@JSONField进行测试:
// BlogType.java
// lombok插件生成get/set方法,以及全参构造方法
@Data
@AllArgsConstructor
public class BlogType {
@Id
private long id;
@JSONField(serialize = false)
private String type;
@JSONField(format = "yyyy-MM-dd HH:mm")
private Date date;
}
// jsonTestController.java
@RestController
public class jsonTestController {
@GetMapping("/index")
public BlogType getTestJson() {
return new BlogType(1, "小强",new Date());
}
}
通过对枚举类的属性进行反射,拿到字段名和对应值来构造返回数据,只是一种思路,没有结合设计原则和设计模式,也跟自己经验比较少的关系,可能有所欠佳,欢迎交流指正!
枚举类:
// RespJsonEnum.java
public enum RespJsonEnum {
SUCCESS(200,"请求成功!"),
ERROR(500,"服务器错误!"),
NO_TOKEN_ERROR(1001,"无token信息,请登录!"),
EXPIRED_TOKEN_ERROR(1002,"token信息已过期,请重新登录!");
private int status;
private String message;
RespJsonEnum(int status,String message){
this.status = status;
this.message = message;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
构造器:
@Slf4j
public class ResponseBuilder {
/**
* 基础json构造器,反射枚举类的字段名和字段值
*
* @param theEnum 消息枚举值
* @return 枚举类 字段名和字段值生成的Map
*/
private static HashMap<String, Object> buildBaseJson(Enum theEnum) {
HashMap<String, Object> baseJson = new HashMap<>();
Class enumClazz = theEnum.getDeclaringClass();
Field[] fields = enumClazz.getDeclaredFields();
try {
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
String key = field.getName();
Object value = field.get(theEnum);
baseJson.put(key, value);
}
}
} catch (IllegalAccessException e) {
log.error("Error:构建基础消息Map时出错:{}", e.toString());
e.printStackTrace();
}
return baseJson;
}
}
有关枚举类的反射有个注意点,枚举类基于对static final的封装,我们打个断点debug一下便一目了然:
不知不觉中,在公司已经实习了一个月了。前几天回了一次学校,这是从学校出来后第一次返校,感受和心情跟以前假期结束后的返校自然是不同的,自次是一次知道会走的返校。看着新一届的新生们眼中带着好奇的目光,脸上带着些许疑惑的彷徨走进校园,我仿佛看到了三年前的自己。匆匆回校,匆匆离别。在学校待了一晚,离开之时正是新生报到的日子,怀着复杂的心情踏出校园的那刻,与新生们的擦肩仿佛是三年时空的错裂,不禁会有些失神。
回校的路是舒适顺利的,和同伴租了车,一路顺风。离开的路却是艰辛和疲惫的,也不知何时习惯了如此,就好像每次回家一样,回家的路总是安心顺利的,听到家乡的方言更是会不自觉的露出笑容。每次离家也总是艰辛和孤独的。也许心里早已把待了三年的学校当成了另一个家,而那帮舍友更是知己。
人总是在路上的,离别也是为了更好的相遇,可能我已经不再属于学校了吧,难免有些感叹~