开发技巧

连接数据库

spring:
application:
name: reggie
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sq1?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&userSSL=false
username: root
password: 168168

mybatisplus配置

mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: assign_id

 

1.配置热部署,不至于每次更改代码都要重新加载整个项目,可以手动配置导入devtools依赖,然后每次更改代码后按ctrl+f9构建项目。

也可以自动部署,有点麻烦

在yml文件中配置devtools下的exclude中可以配置热部署的范围。

 

2.springboot文件中的引用

boot引用单个属性的时候可以使用@value(${})//一级.二级.三级,不支持松散绑定(就是文件中的属性名可以使用驼峰命名也可以使用烤肉串形式,大写小写都可以,包括赋值的字段名都可以大小写

引用文件全部属性·的时候可以定义一个Environment 的变量使用自动加载然后用变量.getProperty(属性)//一级.二级.三级

引用文件的引用类型,可以使用@ConfigurationProperties(prefix=”属性名")//属性名为一级.二级  ,属性名只可以用全小写字母,下划线和数字形式,该注解支持松散绑定,此注解需要写在类上面,并且该类需要被spring管理,就是将此类变为bean对象,使用这个注解的时候需要导入一个spring-boot-configuration-processor依赖

 

3.springboot中jdk8提供的计量单位的使用

@Duration(ChronoUnit.Minutes)   //以分钟为单位。可以更改为hours或者其他

   private Duration Timeout;

@DataSizeUnit(DataUnit.MEGABYTES) //以兆为单位,可以更改其他单位

  private DataSize dataSize;

 

4.bean属性校验

导入两个依赖validation-api,hibernate-validator

在bean对象的类上开启@Validated注解,在需要校验的属性上添加具体的规则,比如@Max(value= ,meassage=“ ”),@Min()

 

5.yml文件中int支持二进制八进制和16进制,8进制以0开头(如果是0127恰巧所有的值都在0-7,那么就会转换为十进制87),16进制以0x开头,如果需要的是字符串直接用双引号“‘   防止进制转换出问题

 

6.测试属性

在@SpringbootTest注解中配置properties值,可以为当前的测试用例配置临时的属性。配置args属性值可以为当前测试用例添加临时命令行参数,执行优先级,args>properties>yml

@SpringBootTest(properties={"test."})

@SpringBootTest(args={"--test."})

 

7.在test类中配置临时的bean

用@Import(xxx.config.class)来导入外部的bean的config类和Spring一样

 

8.test中开启web环境

@SpringBootTest(webEnvironment=SpringBootTest.webEnvironment.Ran.....)

 

9.mybatisplus自动填充,在需要填充的字段上添上注解

@TableField(fill = FieldFill.INSERT_UPDATE)

private LocalDateTime updateTime;

  //此处代表插入和更新的时候使用该字段,也可以单写INSERT或者UPDATE,根据情况写

 

@Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { /** * * MybatisPlus * 插入操作自动填充 *做插入操作的时候会自动执行下面的语句 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { log.info(metaObject.toString()); metaObject.setValue("createTime", LocalDateTime.now()); //这里的方法参数不支持Servletrequest,拿不到session或者request对象,因此可以通过线程, // 发送http时这三个类中的特定方法的线程是相同的所以才能调用 metaObject.setValue("createUser", BaseContext.getCurrentID()); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", BaseContext.getCurrentID()); } /** * 更新操作自动填充 * * * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", BaseContext.getCurrentID()); } }
//忽略这个字段,数据库表中不含这个字段
@TableField(exist = false)

10.消息转换(可以将long或者bigint类型的值转换为String后传输给前端处理,因为如果值过长js会省略后几位,也可以指定传输给前端的日期格式)

public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

//将BigInteger和Long类型转换为String
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)

.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}

需要在MvcConfig中扩展消息转换器MvcConfig类需要继承WebMvcConfigurationSupport重写里面的extendMessageConverters()方法

@Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { /** * springboot如果需要继承webmvc那么就必须配置静态资源映射,否则访问不了 * * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { // 前面的路径是前端访问路径,后面的是映射到本地哪个路径,/不能省略 registry.addResourceHandler("/front/**").addResourceLocations("classpath:/static/front/"); registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/static/backend/"); // registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); } /**; * 扩展MVC框架的消息转换对象 * * * @param converters */ @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { //创建消息转换对象 MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); //设置对象转换器,底层使用jackson将java对象转换为json messageConverter.setObjectMapper(new JacksonObjectMapper()); //将上面的消息转换器对象追加到mvc框架的转换器集合中,其中还有别的转换对象,因此要放到第一个才会优先执行 converters.add(0,messageConverter); } }

 11. 上传下载数据(文件传输一定是MultipartFile类型)

public R<String> upLoad(MultipartFile file) { //原始文件名 String originalFilename = file.getOriginalFilename(); //截取后缀名 String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); //使用uuid重新生成文件名,防止文件名重复覆盖,每次执行的fileName结果都不相同 String fileName = UUID.randomUUID().toString(); fileName = fileName + suffix; //判断文件夹存不存在 File pathname = new File(basePath); if (!pathname.exists()) { pathname.mkdirs(); } //file是一个临时文件,需要转存到指定位置,否则本次请求完成之后临时文件会删除 try { //将文件传到指定位置 file.transferTo(new File(basePath + fileName)); } catch (IOException e) { e.printStackTrace(); } return R.success(fileName); } @GetMapping("/download") public void download(String name, HttpServletResponse response) { try { BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(basePath + name)); //老师这里使用的是ServletOutputStream OutputStream outputStream = response.getOutputStream(); //设置一下响应回去的是什么文件 response.setContentType("image/jpeg"); int len = 0; byte[] byt = new byte[1024]; while ((len = inputStream.read(byt)) != -1) { outputStream.write(byt, 0, len); } inputStream.close(); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
 12.spring提供的一个匹配器
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

PATH_MATCHER (arg0,arg1);
13.过滤器,放行不需要登录信息的页面(须在起动器上配置@ServletComponentScan注解)
@WebFilter(filterName = "logincheckfilter", urlPatterns = "/*")//filterName随便配置 @Slf4j public class LoginCheckFilter implements Filter { //spring下的匹配器,支持通配符 public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; //1.获取本次请求的url String requestURI = request.getRequestURI(); log.info("拦截到的请求{}", requestURI); //定义不需要处理的请求路径 String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**", "/front/**", "/user/login", "/common/**" }; //2.判断本次请求是否需要处理,封装一个方法 boolean check = check(urls, requestURI); //3.如果不需要处理,则直接放行 if (check) { log.info("本次请求{}不需要处理", requestURI); filterChain.doFilter(request, response); return; } //4.1判断登陆结果,如果已登录,则直接放行 if (request.getSession().getAttribute("employee") != null) { log.info("employee已登录,用户id为:{}", request.getSession().getAttribute("employee")); Long empId = (Long) request.getSession().getAttribute("employee"); BaseContext.setCurrentId(empId); filterChain.doFilter(request, response); return; } log.info("employee未登录"); // // //再结合前端好好看看 // // //5.1如果未登录则返回未登录结果,结合前端内容通过输出流方式向客户端页面发送响应数据 // response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))); // return; //4.2判断登陆结果,如果已登录,则直接放行 if (request.getSession().getAttribute("user") != null) { log.info("user已登录,用户id为:{}", request.getSession().getAttribute("user")); Long userId = (Long) request.getSession().getAttribute("user"); BaseContext.setCurrentId(userId); filterChain.doFilter(request, response); return; } log.info("user未登录"); //5.1和5.2合在一块,返回同样的结果 //5.2如果未登录则返回未登录结果,结合前端内容通过输出流方式向客户端页面发送响应数据 response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))); return; } public boolean check(String[] urls, String requestURI) { for (String url : urls) { if (PATH_MATCHER.match(url, requestURI)) { return true; } } return false; } }

14.拦截器,也可以配置拦截页面功能(需要配置两个类一个拦截器,第二个继承webmvcconfigurationSupport或者webmvcconfiger)

/** * 拦截器拦截页面 */ //@Component public class Interceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 2023/2/6 1.判断threadlocal中有没有用户信息
User currentUser = BaseContext.getCurrentUser();
 
if (currentUser == null) {
    // 2023/2/6 2.没有用户信息,拦截
    return false;
}
// 2023/2/6 3.有用户信息,放开
return true
} }


 mvcconfig类

@Configuration public class MvcConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Autowired private RefreshTokenInterceptor refreshTokenInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //登录拦截,order是拦截的优先级 registry.addInterceptor(loginInterceptor) .excludePathPatterns( "/user/code", "/user/login", "/blog/hot", "/shop/**", "/shop-type/**", "/voucher/**", "/upload/**" ).order(1); //登录状态有效期刷新拦截, registry.addInterceptor(refreshTokenInterceptor).order(0); } }

 


__EOF__

本文作者liuliu的小家
本文链接https://www.cnblogs.com/liu-jin/p/17300629.html
关于博主:hello~好久不见,喜欢的话点个赞吧
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Bepowerful  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示